It's...Functional Friday! :party-parrot:
Option, SelectMany, and why C#'s coming nullable reference type is totally meh
null has been called "the billion dollar mistake" (and many say that estimate is low) because of all of the bugs caused by unhandled
null values. Is there an alternative for a variable that may or may not have a value? In a sense no, but what we should have been doing is always being explicit about whether a variable, field, functional return value, etc., is optional or not. If an expression must always have a value, it should not be nullable. However C# references can always be nullable! Same for Java and many other languages. We haven't been explicit. It's like having a piece of dynamically-typed languages mixed in with our static typing.
That's why many languages, functional in particular, are explicit, usually by providing a keyword or type (
option in F#). An
option in F#, Scala, and Java et al, can have either a value of None or Some(value). (Unfortunately for those particular languages listed, they still have to support
null for backwards compatibility. But good practice avoids
null completely. I've done it, and it pays!) So instead of:
Food FindFavoriteFood(int personId)
with the potential of a caller assuming everyone will have a favorite:
we can use
option, and in C# there are several libraries providing such a type. I use nuget package
Option from https://github.com/nlkl/Optional:
Option<Food> FindFavoriteFood(int personId)
Now the caller must handle both the None and the Some case. We could use an if/else to check if a value is there, but the more functional way (as an expression rather than imperative) would be to use pattern matching, also emulated by that library with
some: food => food.Name,
none: () => "I don't have a favorite"))
(Not C#'s pattern matching in this case, but still FP's pattern matching nonetheless.) Now, C#'s coming nullable reference type where you can turn on disallowing null on regular references, kinda solves the problem also. But here's what it's missing that other languages with
Option is IEnumerable. It will have either 0 or 1 elements. That in combination with
SelectMany allows some :
var allFavorites = allPersonIds.SelectMany(FindFavoriteFood))
Catch that? For anyone without a favorite, FindFavoriteFood returns an
Option of case
None which is an
IEnumerable of length 0, whereas those with favorites will be a
IEnumerable of length 1, so stream them all together with
SelectMany and you get all favorites.
These reasons are why my team at Farm Credit started using
Option everywhere a value is optional!
Unfortunately C#'s coming non-nullable/nullable reference types doesn't provide enumeration. Meh.