
Why Maybe Is Better Than Null - eventualEntropy
http://nickknowlson.com/blog/2013/04/16/why-maybe-is-better-than-null/
======
egonschiele
Aha, how timely! I just wrote this post on functors and applicatives that uses
Maybe as an example:
[http://adit.io/posts/2013-04-17-functors,_applicatives,_and_...](http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html)

~~~
nickknw
Cool post! Love the illustrated approach you took.

------
nilkn
In comparison to Haskell, null in Java is similar to adding an implicit Maybe
a instance for _every_ type a.

In doing so, Java makes it just that much easier to create bottom values when
they are not desired (e.g., dereferencing a null pointer).

~~~
gizmo686
I disagree. If null was like Maybe, than you should be able to do:

E foo; ... foo=foo.bar().bat()

without having to do a null check after each function.

I think that Java's approach has the worst of both worlds, because there is no
way to make foo.bar().bat() safe when a function could return null.

In C, for example, calling a method on a null object does not cause an error.
Rather it passes in null as the 'this' value, allowing you to do you null
checks within the method.

~~~
dllthomas
Null is like Nothing, nullability is like Maybe, and (.) has an implicit
fromJust.

------
tikhonj
This article makes it seem like you'd have to explicitly check whether a Maybe
value is Nothing when you use it. This is certainly _safe_ , but it's also
very awkward; as a contrived example, adding two numbers would look like this:

    
    
        case a of
          Nothing -> Nothing
          Just a  -> case b of
            Nothing -> Nothing
            Just b  -> a + b
    

This is quite a bit of boilerplate hiding the expression that actually matters
--a + b! Moreover, whenever you have code that creeps steadily to the right,
it means you either messed up or missed an abstraction.

It turns out that this pattern--do a computation if all the values are
present, but return Nothing if _any_ of them are Nothing--happens very often.
Happily, we can get some nice syntax for Maybe computations like this using
do-notation in Haskell or for-comprehensions in Scala:

    
    
        do a <- a
           b <- b
           return (a + b)
    

This is much better! It makes even more sense for more complicated
expressions, especially when later results depend on values of earlier ones.
However, for a simple example like a + b, it's still quite a bit of boiler
plate; we can certainly do better! Here are two alternatives using functions
from the Control.Applicative module:

    
    
        (+) <$> a <*> b
        liftA2 (+) a b
    

The important idea here is that both versions somehow "lift" the (+) function
to work over Maybe values. This just means they create a new function with
checks for Maybe built in. This is great because it saves all the boilerplate
above and nicely abstracts away most of the null checks while preserving
safety. But the syntax is still a bit awkward. Happily, if you don't mind
using a preprocessor[1], you can get some very nice syntax called "idiom
brackets":

    
    
        (| a + b |)
    

[1]: <https://personal.cis.strath.ac.uk/conor.mcbride/pub/she/>

The computation inside the (| and |) is lifted over Maybe, just like the two
previous examples. I think this is the clearest option here: it has the least
syntactic overhead, and the base expression--a + b--is very easy to read. They
also have the advantage of nesting, so you can express a + b + c, where you
want a null check for all three variables, as:

    
    
        (|(|a + b|) + c|)
    

This isn't _perfect_ , but I think it's still very easy to follow. It might be
better if the (| and |) were a single character, something like this:

    
    
        ⦇⦇a + b⦈ + c⦈
    

However, some people really don't like Unicode symbols in their code :(.
Happily, you can have the source look like (|foo|) and have Emacs replace it
with ⦇foo⦈ without actually changing the code. It's basically Unicode syntax
highlighting. I think this leads to the most readable code so far.

So my main point is that you can abstract out the common case where you check
for Nothing and make the whole expression Nothing if any sub-expression is.
This saves quite a bit of typing and _much_ more importantly makes the
resulting code far easier to read.

Another really cool part is that all these syntax forms and functions are
_not_ specific to Maybe--they actually work for a whole bunch of different
types. So you would not be bloating your language by including special
features just for safely checking nulls; these features are much more general.

~~~
JoshTriplett
You can also make Maybe an instance of various typeclasses for even nicer
syntax:

    
    
        instance Num a => Num (Maybe a) where
            (+) = liftM2 (+)
            (-) = liftM2 (-)
            (*) = liftM2 (*)
            abs = liftM abs
            signum = liftM signum
            negate = liftM negate
            fromInteger = Just . fromInteger
    
        > Just 4 + 2 * Just 6
        Just 16
        > Nothing * 42
        Nothing
    

Notice how the fromInteger method allows you to freely mix Maybe and non-Maybe
numbers.

~~~
notaddicted
I don't have a whole lot of Haskell experience ... but these "Maybe"
unwrapping functions are rare right? Like a null check I'd think that most of
the time you make the check when you get the unreliable input or allocation or
whatever, and thereafter in the guts of you program you have the certainty
that the input has been "checked".

~~~
tikhonj
The real trick is that, conceptually, these are not really "unwrapping"
functions--instead, they're "propagating" functions. All they do is take Maybe
values from deep inside your computation and string them through to the
outside.

In practice, this means that you write a decent part of your program using
these techniques, which creates a block of code that produces a Maybe value
after taking a bunch of Maybe inputs. Then you only use a case statement at
the very end, when you need to plug the Maybe value back into normal code.

All these functions are useful for one particular case: you don't know what to
do with a Nothing value, so if you see one anywhere, you just pass it on: your
final result is Nothing. That pattern just turns out to be very useful.

~~~
aaronblohowiak
>That pattern just turns out to be very useful.

Like NaN, it can be hard to track down where things went wrong.

~~~
masklinn
Except contrary to NaN, the type system encodes where it can exist and what
its span is.

~~~
dllthomas
Right, you can rule out the pieces that are typed as "NaN-free" - hopefully
that's a lot.

This is a great reason to avoid huge chunks of code stitched together staying
inside Maybe, while still being convenient on the small scale.

------
kelnos
So maybe this is just a terminology thing, but, isn't Maybe the same thing as:

1\. By default all types are non-nullable. 2\. You explicitly mark if you
expect that a value could be null. 3\. The compiler helps you by either making
sure you check for null on those values you mark, or by doing some magic (like
Scala does) that will always return null for expressions where one of the
values is actually null.

Is the reason we call it Maybe/Option/whatever just to disambiguate with the
traditional use and lack of safety in what pretty much all languages use
"null" for? Or is there a distinction I'm missing?

~~~
tikhonj
Yes, it's the same idea. The main difference is that Maybe is just a normal
type in languages like Haskell and OCaml--you do not need any especial
compiler support for it.

So to use Maybe, all you need from the compiler is to _not_ have nulls
everywhere. Since you don't need language support for it, your language is
simpler and the Maybe behavior is part of a library.

This also ensures that you Maybe values behave as first-class citizens. You
can do anything with the Maybe type that you could with any other type,
because that's all it is. For example, this means that you can nest them: have
a Maybe<Maybe<A>> value, for example. It also means Maybe can play well with
other libraries; for example, inn Haskell, it works immediately with the
alternation operator:

    
    
        result = tryA "foo" <|> tryA "bar" <|> tryB
    

Part of the beauty is that <|> is an operator that represents alternation for
a whole bunch of other types as well. There are a whole bunch of other
functions like this.

So: yes, you can have language support for it. But just having it as a normal
type makes the language simpler and ensures you have full generality. The only
thing that you need from your language is to get rid of null.

~~~
salmonellaeater
You DO need special compiler support for Maybe! Specifically, you need
algebraic types. Consider Java. There is no pattern matching and no syntactic
construct to decompose algebraic types. So an Option has to be cast to Some
before extracting its value, risking a ClassCastException if the Option was
actually a None. This is just as bad as having nulls, since programmers will
just cast without looking.

~~~
tikhonj
My point was that you do not need to support Maybe _explicitly_. Instead, it
naturally flows from significantly more general language features; Maybe
itself is just a normal type built with these features.

Following your argument, we need special language support for _any_ sort of
abstraction, because everything has to be built in terms of some built-in
language features at some point.

Also, on a largely unrelated note, I think that there is no reason for modern
languages not to have sum types in this day and age. ( _cough_ Golang _cough_
)

------
implicit
I've been working with engineers writing production Haskell for the first
time; its lack of a "null" concept is proving to be absolutely incredible. In
a high-uptime production environment, it's at least as valuable as Haskell's
enshrinement of purity.

We have a convention that all recoverable errors be captured in Either or
Maybe. This policy is paying off in a huge way because the type system forces
us to think about what error cases mean. This is driving us to write
substantially higher-quality code than I've written with other tools.

~~~
nickknw
Great to hear a real-world example of the concrete benefits, thanks!

------
qb45
Another story is that you may end up with something like:

    
    
      Just x -> something x
      Nothing -> halt_and_catch_fire  # impossible
    

In Haskell there is even a standard library function _fromJust_ which does
exactly that.

One introduces this hack knowing that this particular variable will always be
_Just_ and having absolutely no way to deal with _Nothing_ and later somebody
else sees the type _Maybe Foo_ and figures that it must be OK to put a Nothing
in there.

~~~
nandemo
Well, assuming "halt_and_catch_fire" is something like _error foo_ or
_undefined_ , you're essentially going out of your way to circumvent the type
system. It's possible to do it, but it's frowned upon.

------
ExpiredLink
> 1\. The elimination of null. This means that all types (even reference
> types!) become non-nullable.

Yep, problem solved, 'billion-dollar mistake' corrected (at least for future
times).

------
stickfigure
I can't believe there is no mention of Ceylon here. Ceylon has an elegant
approach to this using union types.

    
    
        Typesafe null and flow-dependent typing
    
        There's no NullPointerException in Ceylon, nor anything similar. Ceylon
        requires us to be explicit when we declare a value that might be null,
        or a function that might return null. For example, if name might be null,
        we must declare it like this:
    
        String? name = ...
    
        Which is actually just an abbreviation for:
    
        String|Null name = ...
    
        An attribute of type String? might refer to an actual instance of String,
        or it might refer to the value null (the only instance of the class Null).
        So Ceylon won't let us do anything useful with a value of type String?
        without first checking that it isn't null using the special if (exists ...)
        construct.
    
        void hello(String? name) {
            if (exists name) {
                print("Hello, ``name``!");
            }
            else {
                print("Hello, world!");
            }
        }
    

From <http://ceylon-lang.org/documentation/1.0/introduction/>

~~~
chousuke
That is exactly equivalent to Haskell's Maybe, except I don't know if it's
possible to abstract over type constructors in Ceylon to implement eg. Functor
and Monad.

------
taeric
I think the largest hurdle I have getting running with maybe, is that it just
isn't in my fingers yet. As such, it can really slow you down if you were
trying to build a bare bones prototype without layering. Which, given the
popularity of dynamic languages, I would think is fairly common.

That is, if you are going to use Maybe, you either want to do so from the
beginning, or you want a good layer of abstraction between where the value is
optional and where it is not. Moving something from guaranteed to optional is
a bit more combersome with Maybe.

Also, I have gotten really used to "truthy" values.

------
vorg
> Grōōvy isn’t really about statically enforcing things

They added a statically-compiled mode to Grōōvy last year. Most users like
Grails don't use it yet, but it's there to try out and you can report any
problems to the Grōōvy issue tracker.

------
jayferd
_why, Maybe is better than NULL. You can program without fighting NULL.

------
CJefferson
How many problems are actually caused by null (in particular, how many
billions of dollars?)

While pointers which point into the wrong place for various reasons (off end
of array, previously freed memory) cause horrible issues to this day, I can't
personally remember ever having a serious issue with a null pointer (they tend
to crash quickly and loudly, because in all modern OSes dereferencing NULL
segfaults)

~~~
bajsejohannes
I've worked with a code base in C++ where the code base would accumulate
significant amounts of

    
    
        if (argumentX == null)
            return null;
    

at the top of function signatures. It was just defensive programming. Maybe
argumentX couldn't be null, but it would take time to figure that out
(sometimes I did that, though). More code means harder to read and maintain,
thus costing dollars.

This would also be contagious: If a piece of code checks if X is null, you'll
assume that X _can_ be null, whether or not that's true.

I'd certainly prefer to be able to reason about the code with the safe
assumption that certain things cannot be null.

~~~
kruffin
_I'd certainly prefer to be able to reason about the code with the safe
assumption that certain things cannot be null._

You can do this, but if you want it to be maintainable, you'll also want to
detail in the function comments this technical debt. If you don't, someone
else will come along and see your sweet method (looking only at the comments)
and use it where the input can be null.

Ex:

    
    
      /**
      * This does some stuff.
      * @param entry Does something with this
      * DEBT: Assumes the input entry is not null.
      */
      void doSomething(SomeObject entry) { }

~~~
pubby
Is that C++? Just use a reference if you want a pointer which asserts it can't
be null.

~~~
kruffin
It's Java ;P I haven't used C++ in a long time. But you are correct a
reference does exactly what he wants.

For anyone who needs to look it up like me:
<http://en.wikipedia.org/wiki/Reference_(C%2B%2B)>

~~~
acomar
Except the reference can itself be null, which is still problematic.

------
wellpast
@Nullable plus static code analysis gives me quite a bit of static-time feel
goodness in Java without any need for Optional/Maybe.

------
rtfeldman
Great article! I'm especially glad to see "what have we gained?" in the FAQ,
as it is probably the most frequently asked question I've heard.

OP - I noticed you were thorough enough to mention both Fantom and Kotlin in
one section, so for the preceding section you might want to note that
CoffeeScript also has a safe-invoke operator like Groovy's.

~~~
nickknw
Thanks! And I'll make a note for myself to put CoffeeScript in there too.

------
radicalbyte
In object-oriented modelling you often see the Null Object pattern being used
to the same effect.

<http://en.wikipedia.org/wiki/Null_Object_pattern>

It needs static analysis tools such as Code Contracts (C#) or SpringContracts
(Java) to make it really robust.

~~~
happy_dino
The null object pattern and Maybe/Option are different concepts. The most
important point: A NOP for some type T exposes T's API directly, while
Maybe/Option has its own API (and the type system enforces that Option[T]
can't be treated as a T).

------
jfb
Relatedly, I've always been fond of Objective-C's nil, for languages without
strong static typesystems.

------
pubby
Does that article title pass a type checker? How can you compare a type
(Maybe) with a value (Null)?

Is this instead arguing Maybe vs pointers? No, that can't be right; pointers
are just type-safe as Maybe, albeit less general.

I guess it's a dynamic vs static typing argument in disguise?

~~~
tikhonj
No. The idea is that you can get rid of null-the-value by having a Maybe type.
They serve exactly the same purpose; having a null value in every type is like
making each type implicitly wrapped in a Maybe.

The reason we can compare the type and a value is because they serve exactly
the same purpose in different ways. The core argument of the article is that
we should get rid of null because Maybe does the same thing in a safer way.

~~~
pubby
Pardon my confusion, but is null referring to that of Java-style reference
types, in which case the article is really about those? The value itself seems
irrelevant as null is practically equivalent to Nothing. Now, if comparing the
types then I'd say that Haskell's Maybe has the following improvements over
Java-style optional:

1\. Opt-in

2\. Not tied to reference types

3\. Safe extraction/"dereferencing". ie case expressions rather than Java's
implicit "Maybe t -> t"

All of which aren't restricted to a static type system.

~~~
acomar
Yep, you've got it exactly right.

The problem for dynamic type systems is how do you enforce 3? Do you care to?

~~~
geon
I suppose you could write a sort of safe Maybe template class in c++. You
could pass in two callbacks to handle Just and Nothing respectively. It would
be really ugly, but should work, right?

~~~
pubby
Yeah, a function taking two callbacks is what I had in mind. This works in
dynamic typed languages too, although obviously not as well.

------
anuraj
making null fuzzy may save your program from a crash, but it also makes
execution unpredictable. I would prefer a crash - which would lead to better
localization and bug fixing - than indeterminate behaviour which is hard to
debug and maintain.

------
CountHackulus
Maybe IS Null, they're just two different implementations of monads.

~~~
geon
Not really. Maybe _might_ be null. Null is always null, and it can be
anywhere.

------
Bjoern
Does anyone have a nice implementation of the Maybe Monad for C++ ?

~~~
acomar
There are a ton floating around, they just aren't terribly useful because
every type is by default nullable. So even standard library functions can
return null. You might be able to make your own code a little safer, but you
still have to null check everything.

------
hmottestad
Null != None

Null means unknown.

Take a look at the difference between open and closed world systems.

~~~
geon
Null would mean whatever the programmer inteded it to mean. In the C world it
sees to me it tends to mean "nothing", while in SQL it usually means
"unknown".

------
weakwire
scala did it

------
Evbn
Guava for Java has Optional. Very nice, except for Java's horrid syntax for
generics.

~~~
monkeyfacebag
Guava is definitely cool and I recommend it whenever I can. It's worth noting,
however, that, as a Java library, it cannot provide the compile-time
guarantees mentioned in the post. That is, your Option type could still be
null and so you're still forced to perform run-time null checking. If you want
the full benefit of Option types on the JVM, you might be better off with
Scala.

~~~
grdvnl
The Optional<T> in Guava just forces one to explicitly check for null and then
get the container object during development. A typical pattern would be:

Given an object a of type Optional<MyObject>, we write:

if (a.isPresent()){ MyObject o = a.get() }

Of course, I could still do a.get() without evaluating isPresent() and end up
with an java.lang.IllegalStateException. Here, we are "reminded" to do the
null check.

~~~
mercurial
You can force Maybe in Haskell, and get a bad surprise at runtime as well.

------
dscrd
If you work on types that might be null, you're bloody careful or you're in
the wrong profession. We're not calling incompetent programmers a gazillion
dollar mistake, even though they probably have caused as much.

~~~
venomsnake
Not everyone has the profession of herding spherical cows in vacuum ... in the
real world lots of stuff can bait you in the ass.

~~~
virtualwhys
this...makes me laugh, thanks ;-)

I would agree that the real world can bait one in the ass, out of the blue,
just, what the, things are trying to eat my ass!

As for herding spherical cows and the vacuum, I'll do my best:

    
    
      val maybeCow = Some(1) // SomeOne is the speherical mu (i.e. you)
      maybeCow getOrElse 0 // Cow here becomes 1 with the universe
    

Were maybeCow initialized with a None value, then into the vacuum it
goes...and out it comes with a safe 0 to keep order in our [application]
universe.

