views:

307

answers:

4

Why doesn't F# naturally support a try/with/finally block?

Doesn't it make sense to try something, deal with whatever exception it throws, at least to log the exception, and then be sure that some code executes after all that?

Sure, we can do

try
    try
        ...
    with ex -> ...
finally
    ...

But that seems too artificial, it clearly demonstrates that "F# is against try/with/finally". Why is that?

A: 

But that seems too artificial, it clearly demonstrates that "F# is against try/with/finally". Why is that?

I guess F# might be "against" exception-handling at all. For the sake of .NET interoperability, it has to support them, but basically, there is no exception-handling* in functional programming.

Throwing/Catching exceptions means performing "jumps to nowhere" that aren't even noticed by the type system which is both fundamentally against the functional philosophy.

You can use purely functional (monadic) code to wrap exceptions. All errors are handled through values, in terms of the underlying type system and free of jumps/side effects.

Instead of writing a function

let readNumber() : int = ...

that may throw arbitrary exceptions, you'll simply state

let readNumber() : int option = ...

which makes this point automatically clear by its type signature.

*This doesn't mean we don't handle exceptional situations, it's just about the kind of exception-handling in .NET/C++.

Dario
I do not quite agree with the statement that "there is no exception handing in functional programming". ML programmers have made (good) use of exceptions since the beginnings of the language, even when writing pure functions. It's true that commonly used type systems do not take them into account, but it's so small of a problem that the perfectly fine systems that have been proposed to handle them (one by Yi et al and one by Pessaux et al) have remained academic experiments.
Pascal Cuoq
Nontheless any exception throwing means interrupting the program flow *implicitly* which is therefore inherently unfunctional.
Dario
@Dario: firstly, F# is not a purely functional programming language, not free of side effects. The whole purpose of `try/with/finally` would be to clearly separate *main* code from *recovery* code from *finalization* code, something very present in the non-functional world.
Bruno Reis
Of course F# is not purely functional and as you can see, there is standard .NET exception handling available to some extent (try/catch is even very flexible due to the possibility of pattern matching), but nevertheless certain techniques may be en- or discouraged in the spirit of functional programming (like mutables).
Dario
+2  A: 

I will clarify my comment in this answer.

  1. I maintain there is no reason to assume that you want to catch exceptions and finalize some resources at the same level. Perhaps you got used to do it that way in a language in which it was convenient to handle both at the same time, but it's a coincidence when it happens. The finalization is convenient when you do not catch all exceptions from the inner block. try...with is for catching exceptions so that the computation can continue normally. There simply is no relationship between the two (if anything, they go in opposite directions: are you catching the exception, or letting it go through?).

  2. Why do you have to finalize anything at all? Shouldn't the GC be managing unreferenced resources for you? Ah... but the language is trying to give you access to system primitives which work with side-effects, with explicit allocations and de-allocations. You have to de-allocate what you have allocated (in all cases)... Shouldn't you be blaming the rotten interface that the system is providing instead of F#, which is only the messenger in this case?

Pascal Cuoq
Why CW? If it's your opinion, no need to hide behind it.
Bruno Reis
Anyways, F# is conceived to work within the .NET platform, and is clearly an impure functional language. That said, **no, I shouldn't be blaming the rotten interface that the system is provinding**, but I would really like to understand why does the language seems to go against it.
Bruno Reis
The whole question is pretty subjective and argumentative, and should have been CW in my opinion.
Pascal Cuoq
Not necessarily subjective and argumentative. If the question "should we add or not a try/with/finally block to F#" occurred to the design team, there must be a precise and clear reason to the "no".
Bruno Reis
Ok, ignore point 2 (although that rant kept with the tone of the question, I think). There is still no reason to assume that you want to catch exceptions and finalize around the same blocks. You open a `try...finally` when you allocate a resource, and you `try...with` around the smallest exception-raising block for which you know what to do if an exception happens. There is just no relationship between the two.
Pascal Cuoq
What if the allocation of a resource can throw an exception? That is not rare on the non-functional world (in which great part of F# lives in). In that case, I'd have to do `try/try/with/finally`. Why not simply `try/with/finally`? (and the main question remains)
Bruno Reis
ML, from which F# inherits indirectly, was designed around a small number of powerful, orthogonal features. In my opinion F# preserved the original spirit by not including a construct that would have been redundant with those they were already including. And don't complain, in OCaml we dont't even get `try...finally` (although you can encode it easily with `try ... with e -> ...; raise e`
Pascal Cuoq
I voted the remark "What if the allocation of a resource can throw an exception", but I'm not sure I see the problem. If it's **that** exception you're worried will disrupt the flow of your program, you don't have to finalize, do you? Because precisely for that exception, the resource wold not get allocated. Isn't it rather all the later exceptions that are a problem?
Pascal Cuoq
+5  A: 

Orthogonality? You can simply nest a try-with inside a try-finally, as you show. (This is what happens at the IL level anyway, I think.)

That said, try-with-finally is something that we may consider in a future version of the language.

Personally I have only run into wanting it a couple times, but when you do need it, it is a little bothersome to have to do the extra nesting/indent. In general I find that I rarely write exception handling code, and it's usually just one or the other (e.g. a finally to restore an invariant or other transactional semantics, or a 'catch' near the top of an app to log an exception or show the user diagnostics).

But I don't think there's a lot to 'read in to' regarding the language design here.

Brian
+4  A: 

As somebody already mentioned, you would usually use try-with-finally to make sure that you properly release all resources in case of an exception. I think in most of the cases you can do this more easily using the use keyword:

let input = 
  try
    use stream = new FileStream("C:\temp\test.txt");
    use rdr = new StreamReader(stream);
    Some(rdr.ReadToEnd())
  with :? IOException as e -> 
    logError(e)
    None

I think this is mostly the reason why you don't need try-with-finally as often as you would in other languages. But of course, there are some situations where you may need it (but you could of course avoid that by creating instance of IDisposable using object expressions (which is syntactically very easy). But I think this is so rare that the F# team doesn't really need to worry about this.

Tomas Petricek