Functional Friday 4: Option, SelectMany, and why C#'s coming nullable reference type is totally _meh_

Excited

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:

Console.WriteLine(FindFavoriteFood(123).Name)

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 Option's Match method:

Console.WriteLine(FindFavoriteFood(123).Match( 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 provide: 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 Some or 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.

Show Comments