Null Is Maligned
For the longest time, I was told Null
was bad: “Null the billion-dollar mistake”
But… doesn’t database tables have nullable columns? And when there is no value, I put NULL
in it? If using NULL
is a billion-dollar mistake there, then what do we use instead?
Cue programmers of various unpopular languages to sweep in, “come! there’s no Null here, maybe”
type Maybe a = Nothing | Just a
message1 = Just "Hello, world!"
message2 = Nothing
So… Null
is a mistake but Nothing
is alright? Yes. No.
You see Null
itself is not the problem: When something has no value, it is Null
[1]. That is correct. It ISN’T better if we ignored that correctness and use a 0
instead, or ""
empty string, or 0001-01-01 00:00:00 +0000 UTC
!
ASIDE: Go programmers, life is not a binary choice between “uninitialized C variable causes core dump” vs “implicit zero values then”! [2] Zero values are not our friend: When we unmarshal a json string without error and some numbers are
0
, how do we know if the values were missing (not good) or the decoder saw0
in the json string and decoded it (good) ? We don’t know [3]. Null errors can still be located and fixed, but we can’t locate zero value data corruption like this. And if you argue “Oh but there’s no difference between an absent value and 0” 👀 you should know that’s your Stockholm syndrome talking. [4]
Null
becomes a problem ONLY IF your language lets you use it like it isn’t
maybeUser = findUser users 42 // a nullable reference to User value
maybeUser.name // your compiler is happy; runtime not so much
This convenience of referrencing the name
attribute on a possibly null user
— THAT is the billion-dollar mistake: the null reference. Not Null
itself.
Back to the example of Nothing
being the same as Null
(they are!). The difference is that, in some languages, we can’t use a maybeUser
value like a User
maybeUser = findUser users 42
maybeUser.name
^^^^^^^^^---- compiler error!
The only thing we can do with a Maybe
value is to deal with each specific code path
case maybeUser of
Just u ->
"Winner is " ++ u.name # guaranteed safe access
Nothing ->
"Nobody won"
Inconvenient, but the guarantees are bliss.
So, boys and girls, don’t avoid Null
itself. Don’t make your table column NOT NULL
because of the reputation. Don’t use an inaccurate zero value in place of the correct one. If a column is indeed nullable, be brave and make it nullable.
Add a Null check linter? Change your programming language? Anything palatable to you, but let the Nulls be Nulls.
[1] Or Nothing, nil, et al.
[2] The simplest correct solution to “uninitialized C variable causes core dump” is to require initialization. A little bit of typing goes a long way.
[3] Unless you look into the json string again. 🤦♂️
[4] After all these sacrifices of correctness for implicit zero values to protect against core dump, Go programmers still have to grapple with the nil zero value and all the same nasties as Null… and worse: why is my nil error value not equal to nil?. 🤣