If you’ve coded for long enough in any mainstream OOP language, you’ve probably been bitten by the nasty
The fact that null is the default value for all references has even been called The Billion Dollar Mistake by its own creator, due to the countless bugs it introduced, “which have probably caused a billion dollars of pain and damage in the last forty years” .
The following code depicts a simple interface for a repository:
What’s potentially wrong with this code? As an experienced developer, you may guess that you’ll have to check for null when calling an implementation of this method. But that’s really up to you to remember (or find out the hard way).
A sample usage of the customer repository on a ASP.NET Web API controller method could be something like the following:
It’s a pretty common pattern:
- You retrieve something that may (or not) exist
- If that something does not exist: abort or return a default value (often null again, yikes).
- If that something exists: perform some computation(s) with it (here shown as mapping to a DTO).
- Return computed value.
Functional programming to the rescue
Functional programming has an elegant way to model these scenarios where a function may (or not) have something useful to return: it’s called
Maybe (in Haskell) or
Option(in F#) type.
Unfortunately, C# still doesn’t provide this type for our convenience (unlike Java, which has
However, there are already a few libraries that implement this very well. One of them is
language-ext, which by no means is only around
Option type. It presents itself as a a base class library for functional programming (for C#).
Essentially, an instance of an
Option<T> can either be
None, meaning that the value is absent
Some<T>, you’ve guessed it, there’s a value of T.
Option<T> is a
struct, so an instance of it can never be null.
Sample usage in C#
For any method that returns some instance
T that might exist (or not) you can wrap the returning type with an
Option<T>, as follows:
Looking at this code it’s pretty obvious that what comes out of that method might have (or not) a value. Plus, we’ll have to deal with that scenario, the compiler will not let us ignore that. So, in a way, it’s also a form of self-documenting code (the best kind of documentation).
Refactoring the above shown piece of controller code to use this new version could produce something like this:
Pretty neat, huh?
Map method allows us to perform computations for the
Some<T> case. For the
None case, it simply skips over it. The result of a
Map over an
Option<T> is another
Option<T>, so we can compose multiple
Maps over it.
Match, it accepts two delegates, one for each of the possible cases. The first one (
Ok) is for the
Some<T> case and the second (
NotFound) for the
None case. In essence it’s a functional
if (...) else (...)
Option available methods (besides
IfNone(A noneValue), with which you can provide a default value if it’s the
- you can check the comprehensive documentation here.
I really believe that being explicit about the nature of return types of methods in your code is hugely beneficial in the long run (both for bug prevention and for documentation).
However, this approach has also some drawbacks:
- This may feel a bit alien for some developers (I think this could be mitigated if .NET BCL included this type)
- We have to import some package (or roll our own, if we suffer from NIH syndrome), and make our consumers import it as well. This is why I’m more inclined to use this in the internal details implementation of our APIs (basically, in the domain layer).
If you like this approach, I encourage you to learn at least the basics of functional programming, (there’s so much more than this).
Written by André Carvalho • November 12th, 2018