Home > C#, generics, monads > The Maybe Monad in C#

The Maybe Monad in C#

The Maybe Monad is extremely simple. It represents a value that might be there, but might not, and also provides a neat way of working with such values.

This Haskell-related page makes it pretty clear:

The Maybe monad embodies the strategy of combining a chain of computations that may each return Nothing by ending the chain early if any step produces Nothing as output. It is useful when a computation entails a sequence of steps that depend on one another, and in which some steps may fail to return a value.

Change Nothing to null and we’re talking in C#. Furthermore, it advises:

If you ever find yourself writing code like this:

case ... of
  Nothing -> Nothing
  Just x  -> case ... of
               Nothing -> Nothing
               Just y  -> ...

you should consider using the monadic properties of Maybe to improve the code.

Again, translating into C#, we’re talking about code like this:

public static Role GetRole(string urlString)
{
    string[] url = SplitUrl(urlString);
    RecordCompany company = Music.GetCompany("4ad.com");
    if (company != null)
    {
        Band band = company.GetBand("Pixies");
        if (band != null)
        {
            Member member = band.GetMember("David");
            if (member != null)
                return member.Role;
        }
    }
    return null;
}

As we navigate our way through a graph of objects, we repeatedly have to check whether the road is blocked by a null. When a situation like this occurs inside our nice tidy Linq expression, it makes it look really ugly.

But how can we improve on this in C#?

Firstly, we should be clear that a reference variable (such as company in the above code) is ideally suited for representing the Maybe monad. Any class type can be “stored” in a reference, otherwise that reference has the special value null. So don’t be misled by the example on this page; it’s correct (it’s a great article) but it might make you think that we need to define a special new class to make a monad. In this case, as long as we’re referring to class types (rather than unboxed value types), we don’t need to.

What we’re observing is that reference types already have one of the operations required by a monad: a Unit function. By simply assigning a new object to a reference variable, you are storing that object in a location that might have been null before you assigned to it, and may become null again later on. So assignment to the reference variable is the Unit function for our monad; it’s built into the language.

That’s all very well, but what are we actually trying to achieve? Going by the Haskell description, it’s as if we’d like to be able to casually write a chunk of code like this:

RecordCompany company = Music.GetCompany("4ad.com");
Band band = company.GetBand("Pixies");
Member member = band.GetMember("David");
return member.Role;

If any of those operations returned null, then the return value of the whole thing would be null. But of course we don’t always want C# to apply that rule, and how would the compiler figure out when to stop treating a chain of operations in this special way?

What we’re missing from our monad is a Bind function:

public static TOut IfNotNull<TIn, TOut>(
    this TIn v, Func<TIn, TOut> f) where TIn : class
                                   where TOut: class
{
    if (v == null)
        return null;
    return f(v);
}

The type parameters in this generic function constrain us to dealing with class types, so what we have here is an extension method that applies to any reference variable (weird distinction: it makes more sense to think of extension methods applying to the reference variable than to the object stored in the variable, because it’s perfectly okay to call an extension method on a null reference variable).

This extension method takes a function that “transitions” from one object to another. They don’t have to be the same type. To see the point of all this, let’s see how our ugly code looks if we rewrite it:

return Music.GetCompany("4ad.com")
            .IfNotNull(company => company.GetBand("Pixies"))
            .IfNotNull(band => band.GetMember("David"))
            .IfNotNull(member => member.Role);

Now it’s all just one expression! Cool!

(If you’re wondering why is this important, there are lots of reasons, but here’s one to start you off. The original version of the code was a sequence of statements, so it couldn’t be represented by an expression tree, whereas the new version can be.)

So why is IfNotNull a good Bind function? It’s not immediately obvious that it is, because a Bind function talks to its caller in terms of values wrapped in the monad, but deals in “naked” values with the function passed to it. But IfNotNull uses ordinary reference variables in both situations.

This is because there is a feature missing from C#. It ought to be possible to somehow tag a reference variable to say that it is definitely not null.

Response to comment from Marcel:

The : (colon) operator sounds good, but I’d argue that it’s a little inflexible. It’s a member access operator, like an alternative to the . (dot) so it works for the example I’ve given here. But what if the way I’d like to transition to the next object in the chain is by passing the current object as a parameter to some function? For example, look up the next object in a Dictionary with TryGetValue, using the current object as a key. With IfNotNull, the non-null object is given a named reference so I can do whatever I want with it.

As for verbosity, what I’d really like here is the ability to write what might be called extension operators, something Microsoft has considered but unfortunately isn’t planning to implement anytime soon. These are like a cross between extension methods and operator overloads. This SO question pretty much covers the idea.

If that was possible, we could change IfNotNull to a binary operator such as | (pipe), allowing us to pipe values between lambdas like this:

return Music.GetCompany("4ad.com")
        | company => company.GetBand("Pixies")
        | band => band.GetMember("David")
        | member => member.Role;

I think that would be just about perfect.

Updated June 30th 2009

There are two potential “cultural” problems with the above idea.

Firstly, some people dislike extension methods that are able by design to operate on a null reference as their pseudo this parameter. I generally agree with this, though I’d make an exception if the extension method’s name makes the intention obvious, and IfNotNull makes it perfectly obvious. I’d argue that the BCL’s string.IsNullOrEmpty is another good candidate for making into an extension method, for exactly the same reason.

Secondly, defining extension methods that are too widely applicable is also frowned upon, as they will clutter up auto-completion. Again, I’d tend to argue that the maybe monad is generally applicable with reference types, and so it makes sense for it to appear in auto-completion, but this probably wouldn’t win the argument if you wanted to include this utility in a pre-existing codebase owned by someone especially sceptical.

To bypass these problems, without materially affecting the usability that much, I’ve settled on this version:

public static class Maybe
{
    public static Maybe From(T value) where T : class
    {
        return new Maybe(value);
    }
}

public struct Maybe where T : class
{
    private readonly T _value;

    public Maybe(T value)
    {
        _value = value;
    }

    public Maybe Select(Func getter) where TResult : class
    {
        return new Maybe((_value == null) ? null : getter(_value));
    }

    public TResult Select(Func getter, TResult alternative)
    {
        return (_value == null) ? alternative : getter(_value);
    }

    public void Do(Action action)
    {
        if (_value != null)
            action(_value);
    }
}

Here’s a real example:

IFSRepository repository = Maybe.From(_editorObserver)
                                .Select(o => o.Editor)
                                .Select(e => e.EditingRepository, null);

The naming gives it a Linq-to-objects flavour. As before, you can chain several transitions together, but you have to end with the overload of Select that takes a second argument, which specifies the value to use if the ultimate result is null. The advantage of this is that the two-argument overload can produce either a value type or a reference type, whereas the one-argument overload has to produce a reference type.

The Do method is handy as well, roughly equivalent to the often-suggested ForEach extension method.

About these ads
Categories: C#, generics, monads Tags: , ,
  1. Marcel
    December 12, 2008 at 9:00 am

    Interesting idea with IfNotNull… it might be helpful in some cases, but it’s still too verbose compared with the “:” operator in Chrome (aka Delphi), which is a dot operator that returns null if the left part is null.

    Also, you have a mistake here:

    if (member != null)
    return null;

    return member.Role;

  2. Daniel Earwicker
    December 12, 2008 at 9:21 am

    Thanks, I’ll fix that mistake. I guess that proves my point – it’s worth capturing a common repeating pattern because otherwise you can get it wrong sometimes. So it’s good that I screwed up!

  3. abby, the hacker chick blog
    May 13, 2009 at 1:44 pm

    Neat idea, thanks for pointing me to this.

    I’d still prefer that we just update our code to never return nulls. But, of course, when you’re dealing with existing apps with thousands of lines of code that isn’t always realistic so this is a nice alternative that doesn’t require creating extra classes.

    Now… if only Java would get add extension methods!

  4. June 3, 2010 at 7:29 pm

    This is now possible with the following syntax Music.Maybe(m=>m.GetCompany(“4ad.com”).GetBand(“Pixies”).GetMember(“David”).Role); At least i’m pretty sure this would work with

    http://maybe.codeplex.com

  5. Dax Fohl
    December 18, 2010 at 3:36 am

    This is more monadic, uses LINQ sugar, and doesn’t require a Maybe holder class; these are only necessary in Haskell because it has no null pointers, whereas in C# we can just say null; it’s ultimately like the Maybe (Null) monad type is already built-in.

    I didn’t want to have to create a whole system of business objects that might lead to nulls, so I just created a simple string extension method called NextLetter that returns the next letter of the alphabet, or null if the input is “z”.

    Here’s the code; the syntactic sugared monadic composition grabs the subsequent four letters given the input, and the monad definition simply uses the one SelectMany method that maps to Bind in Haskell.

    using System;

    namespace ConsoleApplication3 {
    class Program {
    static void Main() {
    const string letter = “v”;
    //const string letter = “w”;
    //const string letter = “x”;
    var nextFourLetters = from l1 in letter.NextLetter()
    from l2 in l1.NextLetter()
    from l3 in l2.NextLetter()
    from l4 in l3.NextLetter()
    select new[] {l1, l2, l3, l4};
    if (nextFourLetters != null) {
    foreach (string v in nextFourLetters)
    Console.WriteLine(v);
    }
    Console.ReadLine();
    }
    }

    public static class CharEx {
    public static string NextLetter(this string s) {
    return s[0] >= ‘a’ && s[0] < 'z'
    ? new string((char)(s[0] + 1), 1)
    : null;
    }
    }

    public static class NullMonad {
    public static TResult SelectMany(this TIn @in, Func remainder, Func resultSelector)
    where TIn : class
    where TOut : class
    where TResult : class {
    var @out = @in != null ? remainder(@in) : null;
    return @out != null ? resultSelector(@in, @out) : null;
    }
    }
    }

    As you can see, it works if letter = “v”, but returns null happily if letter is “x”, “y”, or “z”. i.e. it doesn’t get hung up on the middle step being an null value.

    Compare this to something like “y”.NextLetter().NextLetter().NextLetter().NextLetter(). Crash and burn.

    The interesting thing is that VS seems to think (nextFourLetters != null) is always true. But remove the null check; it’s definitely not.

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: