This blog post was originally posted on my blogpost blog at this URL, and was later migrated to this place. There may be some comments at the original URL.
From what I have seen, pattern matching is overused in F#. Especially when dealing with
options. It’s not hard to spot the following kind of pattern in F# codebases:
Here is what’s wrong with this kind of code:
- The intent of the code is not immediately obvious. i.e. low level of abstraction.
- Pattern matching does not compose.
We can alleviate all of the above drawbacks by making use of higher order functions that abstract over common patterns needed for dealing with
For example, with higher order function -
map, the above code can be written more concisely as:
Tony Morris once blogged an
Option cheat sheet for Scala. What follows is a F# translation of the same, with a couple of additional functions.
Several of the following functions are already a part of F#’s
Option module. You should add the rest to your repertoire by extending the
Option module. Alternatively you can use FSharpx which provides most of these (and many other) functions.
Here is some code I wrote not too long ago. (The original was in Scala. Here translated to F#.) The task is thus: You receive a HTTP POST request. The required parameter values come in the URI, and you receive one file as an attachment. If all of these arguments are present and well-formed, you have to compute something, and return it back. This is how the code for this looks with option higher order functions:
In the above code,
attachments are assumed to have types
Map<string, string> and
Map<string, File> respectively.
Int32.parse is a safe string to integer conversion function from FSharpx with type
string -> int option. parseFile has type
File -> string -> int -> Choice<string, string list>.
lift3 lifts it to the type
File option -> string option -> int option -> Choice<string, string list> option.
flip is yet another function from FSharpx, used for flipping function arguments i.e. it turns
'a -> 'b -> 'c into
'b -> 'a -> 'c. respond is assumed to be a function for sending back response, typed
(int, string) -> unit.
Now, my example above also happens to use pattern matching. Yes, there are justifiable use cases for pattern matching on option. This is when you should use it:
- When each case requires elaborate dealing with. Higher order function equivalent of that would be
fold, but it looks uglier if the function being passed is anything more than a one-liner.
- When you are dealing with object graphs more than one level deep. e.g. Matching over
Choice<'a, 'b> option.
- When matching over multiple options. e.g.
int option * int option.
Everywhere else, higher order functions make for more readable code.
(Special thanks to Mauricio Scheffer for reviewing the draft and suggesting improvements.)