I hate null. More specifically I hate NullPointerExceptions(NPEs). Scala has an alternative, but let me explain why I hate NPEs. They're like evil, sneaky muppets. Remember the Martians on Sesame Street? They'd ask each other if a phone was a cow and then say "nope, nope, nope, nope." To my eye, NPE looks a lot like "NoPE."
Me: Is my unit test working?
Computer: NoPE
Me: Um...okay, fixed that problem...how 'bout now?
Computer: NoPE, NoPE, NoPE, NoPE
Me: ARGGHRHGR! You're a Sesame Street Martian!
Computer: Yip, yip, yip, yip1
Does all this code/test/fix/Martians/yelling have to be part of the game? Can't a static typing system save me from this? Can't we stop the Muppets from chanting in my head?
The answer in Java and C# is "No! Well, sorta! Um, no." It is possible to implement a pattern whereby you should never get NoPEs. It's pretty painful to do and the "should" qualifier means it's not really worth it, but it does help illuminate a solution that's available in Scala.2
Some Craptacular Code
First, what's the big deal? Can't I just check for null and deal with it? Here's Java to demonstrate. Any resemblance to a C# code is purely coincidental.
public interface Spaceship { public Martian getMartian(); } // the main method... Spaceship spaceship = getSpaceshipSomehow(); Martian m = spaceship.getMartian() if (m != null) { m.dieDieDie(); System.out.println(m.toString() + " is toast."); } else System.out.println( "No Martians here!"); }
The idea here is that some spaceships have Martians while others don't. If there is a Martian, that little bastard will never NoPE me again. Checking for null certainly works. But there's a problem - how do I know for certain that getMartian can return a null? If I didn't write Spaceship, what made me decide to do the null check? And how do I know that getSpaceshipSomehow() won't ever give me a null if I didn't wite it? The answer is I don't, that's how. It might be in the documentation or might not. The docs might be right or they might not. I might have access to the source code or I might not. I might remember what I wrote last week or I might not. The only answer would seem to be to wait for the NoPE to show up in testing or check for null defensively everywhere. Hmmm...stupid Martians.
Okay, let's try creating a holder that I'll call "Option" for reasons we'll see later. Once again, here's some Java (C#ers please consult your magic decoder rings).
public class Option<T> { private T value; public Option<T>(T value) { this.value = value; } } public interface Spaceship { public Option<Martian> getMartian(); } // the main method... Spaceship spaceship = getSpaceshipSomehow(); Option<Martian> o = spaceship.getMartian(); if (!o.value() == null) { Martian m = o.value(); m.dieDieDie(); System.out.println("I'm a 733T Martian h4x0r." + m.toString() + " has been pwned."); } else System.out.println("W00T"); }
The idea is that API builders indicate nullibility by saying methods return an Option. getSpaceshipSomehow promises that it will return a Spaceship while getMartian may or may not return a Martian. It's okay as far as it goes, but it's entirely too easy to call o.value() without checking for null first. I'd like the type checker to help me here - maybe with subclasses. Here's the attempt in Java. The equivalent C# should be...um...equivalent.
public abstract class Option<T> {} public class None<T> extends Option<T>{} public class Some<T> extends Option<T>{ private T value; public Some<T>(T value) { this.value = value; } public T value() { return value; } } public interface Spaceship { public Option<Martian> getMartian(); } Spaceship spaceship = ... Option<Martian> o = spaceship.getMartian(); if (o instanceof Some<Martian>) { Martian m = ((Some<Martian>)o).value(); m.dieDieDie(); System.out.println(m.toString() + " has been laterally motivated."); } else System.out.println("Thinking outside the " + "box leverages synergies."); }
I've got an abstract base class, Option, that's really just a marker that a value might be "null" or not. None means "null", and Some means it's a real value. Code depending on this structure uses instanceof checks to determine which way to cast.
Unfortunately, this code has three holes and one major (huge) annoyance. The first hole is that the spaceship designer has to ensure getMartian always returns an Option and never a null. If that gets blown, we get NoPEd. The second hole is that there's no way in Java to prevent somebody else from creating a subclass of Option - and that would break the logic. The third hole is that I could just cast an Option to a Some without doing an instanceof check, turning a NoPE into an equally irritating ClassCastException . Finally, the major (huge) annoyance is that I have to write all that crappy code every time I deal with this construct. Frankly, I wouldn't blame you if you'd rather deal with NoPE.
Making it Optional in Scala
In Scala, equivalents to Option/Some/None already exist3 - but they're far, far easier to use and they're "sealed" so no logic breaking subclasses are allowed. Here's the last Java example translated into Scala
trait Spaceship { def getMartian: Option[Martian] } val spaceship = getSpaceshipSomehow spaceship getMartian match { case Some(m) =gt; { m dieDieDie; println(m + " was terminated.") } case _ =gt; println("I'll be back.") }
The Spaceship trait requires all spaceships to implement a getMartian method which may return Some[Martian] or Nothing. Once we have a spaceship we do a match to see if we got a Martian or not, and if we did then it's...retired.
The first thing you might notice is less annotation of types. Let me assure you that the code is just as statically typed as the Java version, it's just that the Scala compiler can figure out the types of "spaceship" and "m" just fine, thank you very much. You may also notice a lot fewer dots, parens, semicolons etc. Scala's syntax allows you to type all that line noise but generally only requires it when things get ambiguous. Finally, in Scala, square brackets work more or less like angle brackets in Java/C# for denoting "generics."
More importantly notice in the "Some" case how cleanly the Martian, m, is "pulled out". The code is easily as clear as the Java version that checked for null. But unlike the null checking Java code it's clear in this case that I have to do the check. If the check wasn't required the code wouldn't even compile. Unlike the Java versions of Option, the Scala version ensures that I've done my checking right.
Confessions of A Leaky Abstraction
Now a bit of a confession. You see, one of Scala's design goals is to interoperate with Java and .Net seamlessly. That's a great source of strength since the libraries for those platforms are huge, the underlying VMs are heavily optimized, and targeting those environments may be a way for Scala to sneak in through the back door of corporate America.
It's also a source of holes. In order to be truly seamless Scala has to have both null and casting. As a result, Scala's Option ends up with some of the same holes as we found in the Java subclassing code. If Scala had tried to plug the null and casting holes, the result would have been an impedance mismatch between Scala and the target VM environments.
Still, the Scala libraries use Option whenever a "null" could be used and Scala projects are following that pattern. As long as you never ever create a cast or a null, you'll only have to worry about NoPE when dealing with existing Java or .Net code. Which is to say, a lot <sigh>. So even though it's not the static guarantee that I want, Scala's Option is incredibly useful since it's just as easy to use as null and far more expressive 5
A Closing Note With Some Rabble Rousing
In Scala, Option is a class and that means it can have methods. One of those methods is a nifty shorthand for getting Some value is available or getting a default value if None. Imagine my Muppet bloodlust (stuffinglust) is so high that if the spaceship doesn't have a Martian, I'll create a new one just to kill it.
val m = spaceship.getMartian.getOrElse(new Martian) m.dieDieDie
Due to another feature of Scala, optional laziness, a Martian is only constructed when needed. Think of it the way an expression might or might not be evaluated in Java/C# when you use logical or. For example, in Java/C# "m = a() || b()" the b() method won't be called if a() is true. Laziness just generalizes this concept.
Option can also be chained with other Option returning expressions using orElse. If I have 3 ships, s1, s2, s3 and I want to get the first available Martian (if any) then
val o=s1.getMartian.orElse(s2.getMartian.orElse(s3.getMartian))
Again, laziness ensures that we only keep looking for Martians until we find one.
Finally, in Scala, Option is a monad. I won't explain all about monads in this article, but I do want to show that Option brings far more power than what I've shown so far. Imagine that from a spaceship I want to get a Martian, get the Martian's ray gun, see who the ray gun's target is, and tell the target to duck. In other words, in Java/C# I want spacehip.getMartian().getRayGun().getTarget().duck(). A nice, easy, one liner. Cool, but now imagine the Martian, the ray gun, and the target are each optional and my rule is that if any of them are null then nobody needs to duck and for God's sake no NoPEs. Urk! In Java/C# you could chain together a bunch of logical ands
if (spacehips.getMartian() != null && spacehip.getMartian().getRayGun() != null && ...
Or you could pile up a series of ifs
RayGun r = null; Target t = null; Martian m = spacehip.getMartian(); if (m != null) { r = m.getRayGun(); } if (r != null) { ...
Either way, it ain't pretty. Because Option is a Monad in Scala the code looks like this
for ( m <- spaceship getMartian; r <- m getRayGun; t <- r getTarget ) t duck
It might not be clear just yet how the "for" keyword is doing this magic, but you have to admit the code is eminently readable. Far more readable than the Java/C# alternatives.
Hopefully this has convinced you that null really should be considered harmful. Just as it's become accepted that there are better alternatives to "goto" there really are better alternatives to "null."
So go spread the word! End the NoPE madness! Monads are the keys to defeating our Martian overlords!
Yip, yip, yip, yip.
Footnotes
1. My apologies to Sesame Street. Realy, sorry, Muppets.
2. There are several languages with the same solution including most notably Haskell. But don't be scared - I won't use the word "monad." Oops, I just did. Also I used it in the title and in the closing section. Oh, and in the next footnote.
3. In Haskell, the Option monad is called Maybe. 4
4. Monad. Just wanted to say "monad" again.
5. Haskell and OCaml don't have these holes since they have neither null nor casting.