For this post I wanted to show you a small yet extremely useful bit of functional programming that you can apply right away to your current C# (or VB.NET) project. While there’s solid theory underpinning it, we won’t need any of that to be able to apply it, so we’ll jump straight to the practice. (I love the theory, so this is quite a sacrifice I’m making for you! ;)) As an added bonus, we’ll also get a good stepping stone for starting to apply more of these practical FP ideas in our everyday code.
Are you null? If not then do stuff!
Dealing with a null is a fact of life in our C# projects. It is not unusual to end up with code that has one or more null checks. Let’s start with a small example:
Here we get the menu from a waiter. If there is no menu available, then we don’t place an order. Otherwise, we order everything on the menu.
There are two issues with this. First is the noise; the guard clause obscures much of what the method is doing. This gets worse as the number of nullable references increases (we can end up with an explosion of guard clauses, or arrow code).
The second issue is that we are using
null to indicate that there is no current menu, which is a value of some significance. We can also get
null because we haven’t initialised a reference, which is generally due to a mistake rather than any particular significance. It would be nice to separate these cases, so that
GetCurrentMenu() can return either
no menu or
Let’s try and address these problems.
Null, or empty?
Rather than treating
GetCurrentMenu() as something that returns a
Menu object, let’s treat its output as a list of menus. This list is either going to contain a single
Menu, or be empty. But we’ll make sure it never returns
We’re now dealing with a list of menus (that just happens to contain 0 or 1 items), so we can use all the standard LINQ extension methods like
Select to transform each item in our list into the required order. Or if no menu is available, we get an empty list of orders back. To complete this step of the refactor, we need to convert our list back into a nullable reference to an
Order, which we do using
SingleOrDefault(). This will return our single order or
GetOrderWhilePeckishmethod to return a list of
Orderobjects to indicate that it too can return no orders. This is a good line of thought! At this point though we’re just making a small refactor; we don’t have to change our callers, including those in our tests.
And because we’re all LINQified now, we can also use the LINQ query comprehension syntax:
It may be a bit hard to see the benefit of this refactor at this point. We’ve eliminated a null check, but the code isn’t significantly simpler. We’ll get to more complex examples in a minute, but first let’s formalise the concept of “none or one”, rather that commandeering lists for that purpose.
A little nuget
It turns out there is already a type in .NET to represent “none or one”, but to use it effectively we’ll want to install a nuget package or two: FSharp.Core and FSharpx.Core (you can also use your existing F# installation instead of the FSharp.Core package).
Then we just need
using FSharpx; at the top of our file, and we’re ready to go.
The type we want to represent our “none or one” value is called
option from F#, and it is exposed to C# as
FSharpOption. The FSharpx library gives us some convenient functions for working with this type. The only changes we need to make to our previous code is to call the
ToFSharpOption() extension method instead of
ToList(), and replace
ToFSharpOption()will work fine on a null reference because it is an extension method; it’s syntactic sugar for calling
FSharpOption.ToFSharpOption(menu), just like our
ToList(menu)call from before.
Logically this works just like our list example. The only difference is we’re using the
FSharpOption<T> type instead of
Different type, same LINQ goodness
Let’s look at a more complicated example. Our restaurant patron is down on their luck, but is desperate for a shot of bacon. They’ll see if the waiter has a menu, and if so then check it for the cheapest item. If that item contains bacon, then we’ll order it. In all other cases our patron will not place an order.
We can convert this from bottom-to-top in small steps, but I’ve found it easiest to start at the top and work down through the whole thing.
This reads much like our initial description of this scenario: from current menu get cheapest item and place and order if it contains bacon. If any of these is
null no order will be placed.
FSharpOption type supports all the standard LINQ:
let-bindings etc, so there is nothing new to learn, we’re just using options instead of lists.
So far we’ve focussed on refactoring the insides of methods, rather than changing signatures. This has simplified our code a bit and also made our intentions more explicit by calling out places where we expect null.
We can start getting a lot more benefit by pushing options into interfaces. Rather than
null or a menu, we can make it return
FSharpOption<Menu>. Callers then have to explicitly deal with the empty case, and the compiler will keep them honest.
FSharpOption<T> in our APIs also clears up our calling code. We no longer need to use
The other thing we gain is the ability to compose these types to perform some interesting operations. For example, say we want to check whether either of two properties is present, we can compose them using
first.OrElse(second), then use
HasValue() to check if either are non-empty, or
GetOrElse(blah) to use access the first value present (defaulting to
There are lots of handy functions on
FSharpOption, but for now I should just note that we can construct instances of options using
myObj.Some() for non-empty, and
FSharpOption<T>.None for empty values.
Gateway to functional programming
As we’ve seen, options can be introduced to clean up some null checks with zero impact on the rest of our code. As we become more comfortable with the ways of using options we can start using them in private fields, then in public interfaces, which starts making our code safer and more intention-revealing. After that, we can start trying other types that FSharp and FSharpx provide, such as
This gives us a great, risk-free way of gradually becoming accustomed to some functional programming techniques. In turn this familiarity gives us new ways of thinking about problems which will undoubtedly come in handy, even if we don’t end up using FP all the time.