Maybe-Monad (Or The Better NullReference)

The Maybe-Monad sounds like some magic, but it’s something terrible simple. Its a computation which returns a result or ‘Nothing’. Often the a null-reference is used to represent ‘Nothing’. This is common practice, but in my opinion this isn’t a elegant solution. First you have to read the documentation to know that the function may return null. Then you have check for null explicit. Furthermore you cannot chain calls anymore since the code is chopped up in check-statements. It would be much more elegant to represent such a computation more explicit. In .NET the Nullable-Type actually allows this. It explicit states that the returned result is either the value or ‘Nothing’. However it only works on value-types, which is rather limiting.

I’ve found this blog entry which introduces a Maybe-class with a lot of useful operations. I always wanted to implement something like this but now I don’t have to. I can just use it and get rid of null-returns.

may-pi

may-pi

The cool thing about the Maybe-class is that it allows you to avoid if-else-checks by using a clear functional style. An example:

// 1. we try to get a person
// 2. we 'convert' to person into an email, but only if it actually has a value
// 3. then we send an email, but only if it actually has a value
Maybe<Person> person = TryGetById(111);
person.Convert(p=>p.Email)
    .Apply(SendMail);

 

Another example with the IDictionary.TryGetValue. The classic-version is really ugly. First it requires an if-else-case. In addition you cannot assign the value directly, which prevent you from using the var-declaration:

public class PersonServices
{
    IDictionary<string, Person> emailTable = // some value

    public IEnumerable<Person> PersonByEmail(string email)
    {
        Person person;
        if(emailTable.TryGetValue(email,out person))
        {
            return person.Children;
        } else
        {
            return new Person[0];
        }
    }
}

Now the Maybe-Version. It’s only 3 lines of code instead of 8. The .TryGet is an extension-method which returns a Maybe<Person>. The next-line gets the children of the person if a person is returned. The last line gets the value or returns a sensible default.

public class PersonServices
{
    IDictionary<string, Person> emailTable = // some value

    public IEnumerable<Person> PersonByEmail(string email)
    {
        return emailTable.TryGet(email)
            .Convert(p => p.Children)
            .GetValue(new Person[0]);
    }
}
 

But certainly you already have code which doesn’t use this programming idiom. But you can create an extension-method to convert reference-types to Maybe-object.

Maybe<AuthContext> LoadAuthContext(long userId)
{
    return oldSessionService.LoadUser(userId)
        .AsMaybe().Convert(usr=>ConvertToAuthContext(usr));
}

Of course this could also be implemented in other languages. In some it would be even more elegant (in the functional ones this is anyway integrated). In others like JAVA the benefit isn’t that great due the inner-classes boiler-plate-code.

And of course use this only when needed. Prefer methods which always return a valid value.

The original code is from this blog-post and the lokad-shared-libraries. (I use a stripped version, since I don’t want to include the whole library). The extension-methods are here: MayExtensions.cs and TestMaybeExtensions.cs

Tagged on:

4 thoughts on “Maybe-Monad (Or The Better NullReference)

  1. gamlerhart Post author

    Learning Scala is on my todo-list anyway. Can you even return a null-reference in Scala?

    Anyway the concept isn’t something new, it’s in many languages.

  2. Mike Murray

    I’ve been reading about Monads, but boy everything I read made it seem so complicated. I liked the idea of being able to set up the rules of the monad and then be guaranteed that every player in that monad would return correct results according to the rules (hope that made sense).

    Either way, this is the first demo I actually really get and can see the everyday application to regular real-world coding problems. Thanks!