Fernando Rodriguez's blog

Code Long & Prosper

nil is not the billion dollar mistake

Wed 10 April 2019

The Null Object Design Pattern and the billion dollar mistake

Since Swift was launched, one of Objective-C's perceived features has come under heavy attack: nil. It has been accused on being the boogey-man that Tony Hoare called the billion dollar mistake.

Hoare's billion dollar mistake

At a conference in 2009, Tony Hoare said:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Many considered this actually meant that having a type to represent the lack of a value was inherently wrong and unsafe, and should therefore be banned from all languages.

Hold your horses. That is NOT what Tony Hoare said, and the Null Object Design Pattern is actually a useful, safe and vital pattern in any language.

This is much easier to understand with an example, so let's try one.

The Null Object Design Pattern

Let's assume we are building a Markdown renderer. You will probably need a parser and then some subsystem to format the text. Some of the formatting possibilities are:

  • bold
  • italics
  • code
  • etc...

A Text Formatter Hierarchy

It might make sense to build a class hierarchy of text formatters. Each subclass is responsible for taking some raw text and outputting the formatted text (let's assume we only care for html, as adding other output formats doesn't change the the design, and can be solved in Objective-C by using double dispatch).

text formatters

This class hierarchy has a small issue: it cannot represent text that has more than one type of formatting!

Such as bold code.

This could be solved by adding another subclass, that implements the Composite Design Pattern

The composite design pattern represents a group of instances that is treated as a single instance.

In our specific example, the CompositeFormatter can represent a set of other instances, such as a BoldFormatter and a CodeFormatter. By joining and applying both, we get the desired effect of bold code.

Composite Formatter

One problem left: edge cases!

There's still one feature in our domain that the class hierarchy doesn't handle well: a common edge case.

Most of the text has no formatting!

Even though the most common case is for text to have no formatting, our class hierarchy seems to assume that this never happens.

The common solution is to fill all your code with if statements checking if the text has to be formatted and if yes, pass it to a formatter.

Something along these lines:

-(NSString *)formatText:(Text*)aText{
    // Will assume that TextFormatter is a class cluster (https://apple.co/2IwWjj4)
    // that can infer which specific subclass should handle the formatting
    if [text shouldBeFormatted]{
        return [TextFormatter format:aText];
    }else{
        return aText;
    }
}

This is idiotic. We are needlessly filling our code with if statements that hide the domain logic and increase the cyclomatic complexity.

By the way, does the code above seem familiar? If you've ever programmed in Java, you've seen this pattern:

if( myVar != null){
    // do something
}

If both pieces of code seem similar, that's because it's the same bad solution to a common problem: checking, over and over, for a common edge case.

As software developers, we should always strive for the simplest solution that does the job. One of the best ways to remove complexity is to remove edge cases.

In OOP, the best tool to remove edge cases is polymorphism

In the example of the TextFormatter the easiest way to get rid of the common case of text not having format, is to create a NullFormatter.

This NullFormatter does something vital: Nothing. Whenever it receives a piece of text, it returns it untouched.

Thanks to the NullFormatter, now there are no exceptions: every text object has a TextFormatter. The common case where the text has no formatting is represented by the NullFormatter.

Now the class hierarchy correctly represents the domain and there's a single rule with no exceptions: every text has a formatter.

Null Formatter

The resulting code is far simpler, and the gazillion if statements scattered through our code have been swapped for a single polymorphic method format: within our class hierarchy.

-(NSString *)formatText:(Text*)aText{  
    return [TextFormatter format:aText];
}

Using the Null Object Design Pattern we reduced the complexity of our code by applying the DRY principle.

The usefulness of the Null Object Design Pattern

  1. The Null Object Design Pattern removes exceptions from the general rules implemented in our code.
  2. It has to be implemented for each class hierarchy.
  3. Within that class hierarchy, it plays the same role as the null element in Algebra (such as zero for real numbers and multiplication)

The Null Object Design Pattern plays the same role and is as vital as 0 is in addition.

Algebra Text Formatting
Elements Real Numbers Instances of Text
Operation Addition format:
Null Element 0 NullFormatter

nil in Objective-C

The catch in this pattern is the same in many other design patterns: it forces the programmer to do repetitive grunt work and creates boilerplate code.

The way Objective-C solves this, is by providing nil: a universal null object that works for any class. It will provide the do nothing functionality by ignoring all messages that are sent to it.

Objective-C is by no means unique here: other languages have taken the same route, such as Smalltalk, Snobol4 and to a lesser extent, Python and Common Lisp.

Recently, many languages influenced by ML (such as Swift, F#, Kotlin, Haskell, etc..) have taken a different route: a parametric (generic) type called Optional or option.

It is however, the same solution to the same problem: provide a way to represent a null element that does nothing, playing the same role 0 plays in Algebra.

So what was the billion dollar mistake?

If the Null Object is not only not a mistake, but one of the greatest ideas ever, what is the billion dollar mistake?

The billion dollar mistake was the way the Null Object was implemented in some languages, notably C.

In C, the Null Object was implemented as a pointer with an invalid address (0 or NULL). It was done so because that implementation perfectly matches the C Philosophy:

  • Simple
  • Fast
  • The compiler trusts the developer

In Objective-C, we had to inherit this decision, because Objective-C was designed to be a strict superset of C.

We are, however, far more shielded by the runtime and Cocoa from null pointer exceptions. With a modicum amount of care, your code should never crash because of that.