Learn to Love Maybe. Kick NULL to the Curb.

 

I recently started learning about the programming language Haskell. I plan to write soon about using it to build a game engine, but before that I want to tackle a neat little feature that's common in functional languages: The Option Type.

In Haskell it's called Maybe. In Scala, 'Option'. Apple's new language Swift also has optional types, with a caveat-- more on that later.

The central idea is that the possibility of having a Null value should be represented at the type level, not at the runtime level.

In C, C++, Obj-C, Java, and other similar languages, the lack of a value is represented by Null. So if your function takes a Thing, you probably need to check if that Thing is really a Thing, or actually a Null (a Nothing). I say probably because sometimes you can be sure you have a Thing, so you don't need to double check. And checking for that is annoying, so it's often left out if possible.

// Hypothetical greeter function
String greet(String name)
{
    if (name == Null) { //Null checking is annoying
        return "I don't know who you are!"
    } else {
        return "Greetings, " + name
    }
}

But sometimes our assumptions are wrong! If we take a function that presumed the value would not be Null, and use it in a new context where the value might actually be Null, we can get a runtime error (hello NullPointerException!). Or maybe we assumed a library function would always return a Thing, but it actually can return a Nothing under certain circumstances. Shame on us for not reading the docs, I suppose.

Dynamic languages like Python, Ruby, Scheme, etc. obviously have no static type checking, so they are prone to the same problems.

The option type solves this problem by requiring us to handle the Nothing case. Here's a quick Haskell example that turns "Axis" into "Greetings, Axis".

First we declare the type of the function.

greet :: Maybe String -> String

In Haskell, a Maybe Thing can either be 'Nothing' or 'Just Thing'. We declare our greet function to be of type Maybe String -> String which means it takes a value that might be a String (or might be Nothing), and returns something that is definitely a String.

Here's the definition of greet:

greet maybe_name = case maybe_name of
    Nothing -> "I don't know who you are!"
    Just name -> "Greetings, " ++ name
    

Because the actual type of the value is a Maybe, in order to get at the Just value we need to use pattern matching. The compiler forces us to confront the possibility of not having a String. Of course, that's a bit verbose. More idiomatic Haskell would do the pattern matching in the actual function parameters:

greet Nothing = "I don't know who you are!"
greet (Just name) = "Greetings, " ++ name

We can simply write two versions of the function. One will do the actual work of the function, and the other will handle the Nothing case.

So what do we win here? I personally like the pattern matching syntax better than

String greet(String name)
{
    if (name == Null) {
        ... 
    } else {
        ...
    }
}

But that's not the big win. The magic is in the return value of our greet function. It's a String, not a Maybe String! That means we know we will always get a String back from that function. You can display it right in a web page without having to consider the Null case. It actually saves us from having to write error checking code in other places. We only write it when the compiler tells us we need to.

So Swift also has this feature, but it makes a small concession to the imperative world of programming. From the Swift docs:

"Of course, if you are certain of the type of the object (and know that it is not nil), you can force the invocation with the as operator."

The question is, if the type says that a value might be nil, how sure can you ever be that the value will never be nil? This sort of assumption may be true when the code is first written, but it tends to break after refactoring. If the value will really truly never be nil, then it shouldn't have been an Optional in the first place.

To be fair, the Haskell compiler GHC only generates a warning for not handling a Nothing case, but I do think that's a big step up from trusting the code as written.

So, embrace the uncertainty! Learn to love Maybe! Kick Null to the curb!