Gerold
Gerold

Reputation: 73

"Lifting" exceptions to Option types

Both F# and Scala act as a hybrid language that is often used to bridge the words of tradional object oriented code to functional code.

A concept that belongs more to the OO world are exceptions, whereas the functional world favors Option types in many cases. To wrap existing library code that relies on exceptions - and make it more functional - I would thus like to "lift" exception-throwing code to instead return an option type.

In Scala, there is a nice library function to "catch all" and convert to option. It can be used like this:

import scala.util.control.Exception._
val functionalVersion = allCatch opt myFunction

see In Scala, is there a pre-existing library function for converting exceptions to Options?

Now that I'm moving to F# I have the same requirement, but I can't seem to find an existing utility function for this - and also struggle to implement one myself.

I can create such a wrapper for a unit function, aka an action

let catchAll f = try Some (f()) with | _ -> None

But the problem here is that I don't want to first wrap all the exeption throwing code into an action.

For example I would like to wrap the array-indexing operator, so that it doesn't throw.

// Wrap out-of-bounds exception with option type
let maybeGetIndex (array: int[]) (index: int) = catchAll (fun () -> array.[index])

maybeGetIndex [| 1; 2; 3 |] 10 // -> None

However, it would be much nicer if one could simple write

(catchAll a.[index])

i.e. apply catchAll to a whole expression before it is evaluated. (Scala can achieve this through call-by-name parameters which seem to be missing from F#)

So this question is twofold:

  1. Is there an existing library function to wrap exceptions into option types?
  2. Is there a language feature that would allow me to implement it?

Upvotes: 3

Views: 384

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243051

First of all, I think that it's not true that a "concept that belongs more to the object-oriented world are exceptions". Exceptions exist in many functional languages of the ML family and, for example, OCaml relies on them quite heavily and uses them even for certain control flow structures. In F#, this is not so much the case, because .NET exceptions are somewhat slower, but I see exceptions very much orthogonal to the object-oriented/functional issue.

For this reason, I actually find exceptions often preferable to option types in F#. The downside is that there is less type-checking (you do not know what might throw), but the upside is that the language provides a nice integrated langauge support for exceptions. If you need to handle exceptional situations then exceptions are a good way of doing that!

To answer your original question about syntactic tricks you can make - I would probably just use a function as your existing code does, because that's explicit and easy to understand (and presumably, you'll only need exception wrapping in some core functions that you implement).

That said, you can define a computation expression builder that wraps the code in the body and serves as your catchAll function with a somewhat neater syntax:

type CatchAllBuilder() = 
  member x.Delay(f) = try Some(f()) with _ -> None
  member x.Return(v) = v

let catchAll = CatchAllBuilder()

This lets you write something like:

catchAll { return Array.empty.[0] }

As mentioned earlier, I wouldn't do this because (i) I don't think all exceptions need to be converted to options in F# and (ii) it introduces unfamiliar syntax that new team members (and future you) might be confused by, but it is probably the nicest syntax you can get.

[EDIT: Now a working version with return - this is somewhat less pretty, but perhaps still useful!]

Upvotes: 5

Related Questions