I call it my billion-dollar mistake.
Sir Charles Antony Richard Hoare

Sequoia Gegant

This quote quickly became famous amongst static typing adepts, pure functional programming evangelists and many other cults disciples. The truth is Tony Hoare never mentioned nil value. The full quote is:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965.

Emphasis is mine. Null reference! It has nothing to do with null in Java, which is a pass-by-value. But Kotlin authors seemed to not bother reading the whole quote and they indeed re-invented the wheel.

Kotlin makes a distinction between nullable and non-nullable data types. All nullable objects must be declared with a “?” postfix after the type name.
Kotlin @Wiki

I doubt there are any reasons of doing that besides the religious worship to the strong typing hype.

The truth is in the real world everything is nullable. When I come home in the evening my fridge might serve me a beer, a sausage, or an apple — depending on how good was my day. When I am back from my vacations, there is nothing in the fridge. None. Nihil. Nil. Null.

I cannot tell (and nobody could,) there are three different nils:

  • nil of nullable beer,
  • nil of nullable sausages,
  • nil of nullable apples.

5 years old would tell you this is bullshit. There is just one nil in my fridge, meaning there is nothing in there.

And here we come to the main mistake people using languages having nil objects make all the day. Developers tend to distinguish “no value” and “nil value.” Which is a complete nonsense either.

Joe Armstrong nailed the proper way of developing computer languages:

Once you’ve split the world into parallel components, the only way they can talk to each other is through sending messages. This is almost a biological, a physical model of the world. When a group of people sit and talk to each other, you can view them as having independent models in their heads and they talk to each other through language. Language is messages and what’s in their brain is a state machine. This seemed a very natural way of thinking.
Let’s #TalkConcurrency with Joe Armstrong

This is applicable to the software development process in general. Abstractions should be as adhered to our real life as possible. The Nature in any case does it better than we.

I’ll tell nobody, feel free to share: did you ever carefully handle nils in your hashmap values using Hash#fetch or how would it be called in your language of choice?

foo = hash.fetch(:foo) { |key| raise "Key #{key} is missing!" }

If the answer is yes, you are doing it wrong. I have been there for almost twenty years too. I carefully handled corner cases, I kept nils where they belongs, after all one still might query Hash#keys and we must return those existing but having nil values.

Nope. hash[:foo] is good enough. And the healthy Hash#keys function should filter out the keys having nil values:

def proper_keys(hash)
  hash.map { |k, v| k unless v.nil? }.compact

Let me state it again: nil means an absense. Value nil means there is no such thing. Absense of a key means there is no such thing. They are identically equal. That simple. If you are using nils for denoting somewhat else, you are doing it wrong. Somewhat else desires it’s own type. If you need to explicitly state somewhat else, create a typed object and make it having a state. But when there is no such object, nil is pretty good.

Purists loudly advocate nullable types, monads, all that crap,—might not only help us to catch errors on compilation stage (static code analysis stage for interpreted languages,) but it even might obsolete tests. Well, maybe. In academical research papers dealing with toy datasets. But in the real life, the code as νούμενον, as Kant’s Ding an sich makes a little sense. To bring a value, the code must deal with some input data. And unless you are a prison director, you are hardly able to convince your users bringing input data to always provide a bullet-proof valid datasets.

So the validity check would be still needed. And instead of dealing with enormous boilerplates of instances of nullable types, just resort to nil. That is my gained by blood and sweat advise: use nils to denote an absense. Because an absense of a lemon does not differ from an absense of two apples. We do not receive from the user the nullable absense of password. We just do receive nothing. Maybe they wanted to send us a token instead, but failed. The intent does not matter at all. If params['password'] returns nil, we should consider the data lacks a mandatory field. No matter whether we have a key 'password' in our hash with nil value, or we don’t have such a key at all.

In Ruby we can check if the value is there explicitly with foo.nil?. In Elixir we might pattern match on nils:

def foo(nil), do: :error
def foo(value), do: {:ok, value}

Even Javascript allows an explicit check for null/undefined (having both is not a billion-dollar mistake, but it is surely a mistake that is worth a couple of grands; null is literally undefined.) But people continue shooting their own legs overcomplicating natural things and bringing stillborn abstractions like Null object pattern, Rails Object#try method, C#/Ruby2.5+/Kotlin/… safe-call operators, etc.

There is no scenario in which the following method should be called if there is a value, and simply ignored if there is not. Well, actually I could come up with some contrived examples, like “send email if the address is there, do nothing otherwise,” but it is still a code smell and a clear sign of a design flaw. If we are to perform an action on presented value, we likely are to take some other action on it’s absense.

Either we want to send an email, and then we should enforce the user to provide it, or we don’t bother and the whole call is redundant.

Yes, I am exaggerating a bit, but in general it works that way.

Use nil where it belongs to. Don’t let religious fanatics to spread their monads everywhere. Types are fine when they are applied as @specs in Elixir. Aside. Waiting there for the static analysis. Not sticking a stick in the wheels and not getting in our way. We are mature enough to decide ourselves, whether we want to allow nil, or gracefully reject, or fail fast, or whatever.

Happy nulling!