Edit: As of C# 8, nullable reference types provide a functionally equivalent implementation of option types in a far more elegant way.
Option types have been in Java, Haskell and a lot of other languages for as long as I can remember, but sadly, they haven't officially made it to .NET yet.
I've been using Maybe<T>
, my own implementation of an option type, in .NET for quite a while now, and have recently decided to turn it into a GitHub repository, mainly because I find myself copy pasting those two classes virtually every time I work on any WebAPI related project.
Without diving too deep in the null is a bad thing discussion, I'll outline what option types are, and why they may be useful when developing software in this post.
What is an option type, and why do we need them?
Simply but, an option type is a struct
with two properties: HasValue
and Value
. An option type encapsulates either a value, or no value. The goal of the option type is to abstract null
away, since, as outlined in the post above, the meaning of null
is a rather ambiguous one.
The reason we "need" them is quite simple. Consider the following snippet:
What would happen if we suddenly decide we need the first element form this list, and we call .FirstOrDefault()
to obtain that value? We'd get null
. Null is the first item in the list, but we can't possibly know, it could also be returning default(string)
.
This is where option types come in: they either represent the value, or no value, but you're always certain which of the two.
Another example where option types would fit nicely into the BCL would be the various TryParse
methods. For instance, int.TryParse(string value, out int result)
returns a bool
indicating whether the parsing operation succeeded, and passes the result back to the caller as an out
parameter. With option types, we can do the following:
Want to know if the parsing operation succeeded? Check if HasValue
is true. Instead of a return type indicating success combined with an out
parameter indicating the value, we now have a single return type, and it says it all.
Isn't this the same as Nullable?
Yes, sort of. Nullable<T>
is an option type, but unfortunately it only works on value types. It also gets a little bit of special treatment in the CLR in that it will never be boxed; either null
is kept as a reference or the value of the nullable instance is kept, but never the nullable itself.
Oh, it's also directly assignable from T
, which is something you can't achieve without the compiler really liking you either.
So null is always a bad thing?
Well, no, it doesn't have to be. You see, I started off this post mentioning that I don't want to dive into the whole "null is bad" debate, but I guess when discussing a solution to the "null problem" (I don't think null
itself is a problem, by the way), you can't really keep your feet completely dry. I'll dip my toes a little.
In C#, null
indicates a lack of value. That's great, because sometimes we need to indicate that we don't have a value. In the example above, we made a call to FirstOrDefault()
which returned null, because it was the first value in the collection, but, as the caller, we can't really make out whether it returns default(T)
or the actual first value.
Regardless of whether or not this is a situation you'll run in to very often, we can conclude that null
does have a verbosity problem: it doesn't convey enough about what that null
value you're seeing means. And that, in my opinion, is the problem option types solve.
Having said that, and adding to the ambiguity of null
, this verbosity problem doesn't apply when properties or fields aren't set to an instance of their respective types. Given a property along the lines of:
public IComponentRepository ComponentRepository { get; set; }
null
is very concise: ComponentRepository
does not refer to an instance of an object, which is perfectly captured in a NullReferenceException
. There are no two ways about it.
Conclusion
Option types, or Maybe<T>
in my implementation, aim to solve a problem where null
as a return value isn't concise enough.
Don't go about trying to abstract null out of the language completely. It has its place, but there are definitely occurrences where conveying a little bit more information would come in handy, and that's where we apply our option types.
Thanks for reading!