Introduction
In a previous blog post, we ventured into the intriguing world of the Option module, offering but a fleeting glimpse of Option.map
and none of its close companion, Option.bind
. In this blog post, I will take the opportunity to talk a bit more about the Option.bind
function and show you how helpful it can be when dealing with optional values.
What are they?
Option.map
A higher-order function that allows us to apply a given function to the payload inside an option. It takes an optional value, applies the provided function to its inner value if it is Some
and returns the transformed value wrapped back in the option. If the input is None
the function does nothing and returns None
.
The function signature is as follows:
('T -> 'U) -> 'T option -> 'U option
We give it a function ('T -> 'U
) followed by the option we wish to transform with that function ('T option
) and it returns an option but with the altered value wrapped inside ('U option
)
Option.bind
Let's look at the signature first:
('T -> 'U option) -> 'T option -> 'U option
While the behaviour seems similar, the transformation (also called binding) process is handled differently, the binding process returns an Option
value instead of just the value.
The difference
Option.bind
is used when you want to chain multiple operations that return options together; each operation might succeed (returning Some value
) or fail (returning None
). The chain will "break" after the first None
as every operation after it will use None
with every operation not doing any processing. This process is similar to Railway Oriented Programming.
Let's think in which cases Option.bind
might be useful. Imagine you need to find the middle name of the author by looking at the sequel of a book, but we don't know if that book has a sequel or that the author of that sequel book has a middle name, in that case we need to use Option
when representing them in our code.
The flow:
- Find the sequel (
Option
) - Find the middle name (
Option
)
let getMiddleNameFromSequel (book:Book) =
let middleName =
book.Sequel
|> Option.bind (fun s -> s.Author.MiddleName)
match middleName with
| Some name -> $"The middle name is: {name}"
| None -> "Cannot find the middle name"
let author1 = {
MiddleName = Some "John"
}
let sequelBook = {
Sequel = None
Author = author1
}
let book1 = {
Sequel = Some sequelBook
Author = author1
}
In this part
|> Option.bind (fun s -> s.Author.MiddleName)
If you use map
and not bind
, you might end up with option<option<string>>
which would be quite unpleasant to handle.
Conclusion
In this blog post we looked at Option.map
and Option.bind
and tried to understand their difference and when to use which. Again, I would like to recommend "Stylish F# 6" by Kit Eason which provides more details about handling missing data.
I hope you found this blog post useful 🙂 More stuff about the Option
module might soon follow!