The Option Module
The Option type is a well known and often used type, but at least for me, most of the
time I just used Option.map
and Option.bind
and ignored functions like Option.exists
,
Option.filter
, Option.fold
and so on. I spent some time with those functions to understand
when those are useful.
defaultArg
The first function i look at is actually not in the Option module. It is the defaultArg
function. With defaultArg
we can extract an option type and provide a default value
in the case we had no value.
|
|
One think I dislike is the order of the arguments. Because the Option type is expected first
as an argument, defaultArg
is unsuitable for piping or composition. That’s why I most often
add a orElse
function to the Option module myself.
|
|
exists & forall
I must admit, i never looked closer at those functions. It is obvious that those functions are ported from the List/Array/Seq module. But because an option never contains more than one element, I never looked closer to those functions. The truth is, because we know that an option only contains either no value or a single value, the meaning of those functions just change.
Let’s look at some typical code with no option at all that you will sometimes have. You just check a variable if some statement is true or false and you use that for branching.
|
|
What do you do, when x is an option
? Then you can use Option.exists
|
|
Generally speaking. With Option.exists
you can check an option for a condition. None
is treated as false
. Naming the function check
or some other name than
exists
would probably have been a better name. With some helper functions we can enhance
the validation process.
|
|
Option.forall
is basically the same, only that None
is threaten as true
instead of false
.
But I must admit, I cannot come up with a useful example for forall
.
filter
In my last example I added a second check. While two checks are still somehow okay in terms of readability it can become unhandy fast. Wouldn’t it be better if we could chain the operations?
filter
gives us exactly this ability. Instead of returning true
or false
it just returns
an option again. When the predicate we provided returns true
we just get back the original value
unchanged. Otherwise we get None
.
|
|
We also can use Option.filter
to easily turn a type into an option based on a predicate.
|
|
fold
I started with defaultArg
and implemented orElse
. But overall we could replace those with
fold
. Besides the option itself, fold
expects two additional arguments. A function and an
accumulator. fold
either executes the function or it returns the accumulator as the default value.
|
|
In general the idea of fold
is that we can return any other type that we want. fold
is
a general way to convert types. If that sounds a lot like map
. The difference is that map
still only converts the wrapped type and we still get an option back. But with fold
we
directly get the wrapped type back. It just means that whenever you use a map
and then
orElse
. You also could use fold
instead.
|
|
Up to this point I always ignored the accumulator argument, and just used the accumulator
as the default argument. But in general it means whenever you want to use a function
where one argument is an option you could probably use fold
. In general fold
works
nicely together with binary functions.
|
|
We either execute our function with two arguments, or if the second argument is a None
we return
the first argument as the default value. The arguments itself don’t need to be of the same types.
Validation
With very few helper functions we could built a small validation framework that uses the option type.
Most of them are just other names instead of map
, filter
or bind
.
|
|