
Null References: The Billion Dollar Mistake – Tony Hoare (2009) [video] - quickfox
https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare
======
Perceptes
One of my absolute favorite things about Rust is that nulls can't be used to
circumvent the type system. They must be explicitly accounted for via an
option type in all cases. It's very hard to go back to a language that doesn't
do this after getting used to it. I get so frustrated by constant null
dereference errors when that entire category of error can be avoided.

~~~
ArkyBeagle
I'd rather have explicit seperation of concern for UB-causing behavior than
have a generic monad approach.

Perhaps the examples I've seen in Rust just don't do that, because part of the
appeal of Rust is that you sort of don't have to.

That makes the cognitive load acceptable and documents all the UB prevention
in source code. It's a habit arrived at from using formal instantiation
protocols that were driven from external to the device being made - starting
with RFC1695.

------
meddlepal
The problem isn't null itself, but that many languages use nullable types by
default.

Null is a damn useful construct and while there may be better solutions to
using a null reference they're often not as clear as using null or worth the
time investment to develop properly.

So yea, I generally try to avoid null's and completely understand how they've
caused a lot of problems, but I'm not willing to say we shouldn't have them or
use them when appropriate.

~~~
kazagistar
You don't just not use nulls, you replace them with something like Optionals.
With a bit of language support, they can compile down to nulls but provide a
layer of type safety and convenience functions.

~~~
kmiroslav
It's perfectly fine to use nulls in languages that encode them in their type
system, e.g. Kotlin.

~~~
junke
Exactly. The problem is not that there is a null, it is that it is a valid
value for all types (e.g. you can pass null to a function that expects a
String).

Allowing nulls explicitly, like with a "String?" type, is the way to go in
languages where Option is not an option.

~~~
kqr
Isn't it, in practise, basically the same thing though? The theoretical
difference is this:

1\. Option is a regular data type/object that represents "a collection of at
most one value". It's similar to a list, set, heap, tree or whatever other
collection types you have, except it can not contain more than one value.

2\. With explicit nulls I guess any data type (e.g. Integer) will
automatically get a clone of itself (a different type named Integer?) where
also null is a valid member.

From a purely theoretical standpoint, I like #1 better.

~~~
SatvikBeri
I think Option[String] and "String?" are semantically both a union type–they
both effectively add one possible value to the underlying type, along with a
constructor for naturally extending functions on that type–Kotlin's "?"
operator is equivalent to Scala's map or Haskell's fmap, etc.

~~~
airless_bar

      Option[Option[String]] != String??

~~~
SatvikBeri
My impression, though I'm not sure, is that Kotlin simply won't allow you to
do a double "??". Scala etc. will technically allow Option[Option[Something]],
but in practice you would almost never want to use it, and can easily avoid it
with flatMap.

~~~
airless_bar
The whole point of this was to show that Scala's types preserve the structure
of the computation.

It might not be very interesting in the Option[Option[String]] case but
imagine Try[Either[String, Int]] or List[Future[Double]].

It's a very important distinction.

Collapsing cases is one of the primary thing why exceptions sometimes get a
bad rap, and Kotlin (and Ceylon) do the same with ? (and |, &) at the value
level.

------
randiantech
Just to add my two cents to the discussion, Typescript is implementing Non
nullable types [1]. I think i'd be convenient that any language being used for
large projects would support this feature.

[1]
[https://github.com/Microsoft/TypeScript/pull/7140](https://github.com/Microsoft/TypeScript/pull/7140)

~~~
kaeluka
Some functional language use non-nullable types by default. Haskell, ML,
OCaml, f.ex.

Java has the checker framework that supports `@Nullable` annotations and will
allow only those vars/fields to contain `null`.

------
skoczymroczny
Why are option types that force you to check the value for null good, but
checked exceptions are frowned upon?

~~~
kqr
The arguments I've heard against checked exceptions are that they're hard to
compose.

If you write the `send_mail` procedure you might only want it to throw
`MailException`s of various kinds. But if you use a TCP procedure inside,
you'll have to declare `send_mail` to also throw `ConnectionError`s, thus
revealing its implementation. To correctly hide/abstract over the
implementation, you have to internally _catch_ any `ConnectionError`s thrown
by the TCP procedure, and re-throw them as `MailException`s. That's a lot of
manual work that shouldn't be needed.

Another similar problem is what someone else mentioned: if you don't actually
know which specific exceptions your method can throw until runtime, what do
you declare?

\----

 _Note that this is also the case for some "null alternatives"_. Sure, the
`Option`/`Maybe` type is easy to compose, but as soon as you start inserting
error information in there (with an `Either` type) you run into some of the
same problems. This is acknowledged by language communities where that is
practised, and some of them prefer unchecked exceptions to `Either`-style
types for that reason.

~~~
ArkyBeagle
Unless there's a full discipline built into the system for addressing TCP
errors that your "mail" thingy can rely on, you'll need to address it.

And how can you _not_ know the specific exceptions you need to handle? Don't
you need to test all of those?

I am sure you didn't mean it to, but this reminds me of meetings where people
say "oh, we don't have to worry about that. TCP is reliable."

 _Twitch_ :)

------
SuddsMcDuff
I posted a very closely related article about this last year
([http://mattdavey.logdown.com/posts/259807-avoiding-the-
billi...](http://mattdavey.logdown.com/posts/259807-avoiding-the-billion-
dollar-mistake)) - it's C# focused but the idea should carry over to similar
languages. It summarizes 5 strategies for dealing with nulls, rather than just
jumping straight into monads.

~~~
ppcsf
I think the section dealing with the Optional monad sort of glosses over their
composability, which is one of the primary reasons we use monads. Multiple
functions that all return an optional can be composed; for instance, if we had

    
    
      Optional<WidgetFriend> GetWidgetFriend(Widget widget)
    

and WidgetFriend had an Optional<FriendName> property, we could simply write
[1]

    
    
      from widget in _widgetRepository.FindById(widgetId)
      from friend in _widgetRepository.GetWidgetFriend(widget)
      from name in friend.Name
      select name;
    

and get an Optional<FriendName>, without having to repeat all the tedious
pattern matching of Some or None for every call. If any of those calls returns
a None, the entire evaluation short-circuits and we get a None result at the
end.

Of course, we can still write

    
    
      Optional<Widget> widget = null;
    

which is the problem with not fixing this issue on a language level.

[1] You'd have to implement Select and SelectMany, instead of Map and Bind, to
get query syntax like this.

~~~
achr2
I think you'd like C#'s linq syntax and null coalesce operator. While
obviously they don't get away from null, they go a long way to making this
type of syntax and null safety much easier to implement.

------
igravious
So that's the famous Tony Hoare. Nice trip down memory lane there.

Who was the guy at the end from the Erlang community? (The one who drew a
chart with the axes useful/useless versus unsafe/safe that he claimed he got
from Simon Peyton-Jones?)

------
bogomipz
I have a question about null being able to subvert types, using Java an
example, if I write foo.upperCase() and foo is a string, the type check
succeeds but will produce an NPE at runtime. Why doesn't javac catch that foo
is not set i.e is "null." Why is null above type checks?

~~~
zaphar
Because whether foo's _value_ is null is for _most_ cases only detectable at
runtime precisely because java doesn't have the capability to express disjoint
type sets in it's type system the way that say Haskell or F# do. This
inability means that the programmer can't tell the compiler what code paths
produce null vs what code paths produce string. As a result the compiler has
to assume that all codepaths could produce both.

There are some trivial cases where the compiler could figure it out for you
but most of those cases are not very useful in practice.

~~~
bogomipz
Interesting, is there a formal name for this kind of type system that has the
ability to look at the code paths and express disjoint type sets?

Is this a trend in newer languages in general or an FP specific thing?

~~~
zaphar
Typically it's referred to as an Algebraic Data Type
[https://en.wikipedia.org/wiki/Algebraic_data_type](https://en.wikipedia.org/wiki/Algebraic_data_type).
Any Type System that can express this type will be able to do the above.

It's not necessarily new since the ML family of languages has had it for a
while now. It's also not limited to FP languages but it best known from ML the
ML family. Rust has it though as well as Swift i think.

~~~
bogomipz
I see. Thank you for the follow up. This is really helpful. Haskell implements
this data type which is why it has come up in this discussion. I need to spend
some time with something from the ML family.

------
chvid
Nulls are not a mistake. They are a trade-off as so many other things in
language design.

~~~
virtualized
Benefits of nullable by default: -

Drawbacks: numerous

How is this a trade-off?

~~~
chvid
Null as a default is useful / a big simplification when it comes to
reflection/meta-programming.

Consider this piece of pseudo-Java/Spring code and think how you would do it
if the platform you were using forced you to either declare a and b as
optional (which they are not) or assign them some value between the IoC
container instantiates the class X and wires the values for a and b.

    
    
       public class X {
           @Autowired
           ServiceA a;
    
           @Autowired
           ServiceB b;
    
           @PostConstruct
           void init() {
               // a and b are instantiated by the IoC container and
               // are fully valid for the rest of the execution
           }
       }

~~~
ppcsf
Constructor injection?

~~~
chvid
Yes. But then you have to define an explicit constructor. Also X maybe auto
wired into ServiceA or B making it impossible for the IoC container to create
the intended object graph without splitting construction and injection into
two passes.

~~~
yazaddaruvala
1\. Lombok @RequiredArgsConstructor?

2\. Do you not consider cyclic dependence a code smell? At least I have always
avoided cyclic dependencies.

~~~
chvid
I personally consider the example I gave as being a nicer way of doing
dependency injection than the more verbose constructor based injection.

But in general yes; cyclic dependencies are a bad idea.

------
elchief
You're gonna need nulls until databases stop supporting nulls. And everyone
would need to use 6th normal form for that to happen, and nobody wants that

~~~
bbcbasic
.NET uses DBNull.Value, which is not null.

Also 6th normal form?? What is that?

~~~
icebraining
[https://en.wikipedia.org/wiki/Sixth_normal_form](https://en.wikipedia.org/wiki/Sixth_normal_form)

Not something I'd like to use.

~~~
achr2
I work with an engineering application that uses a 5th/6th normal form
database that is implemented in standard SQL server/Oracle DBs. It is awful
and painful. A simple query with maybe five properties and 2 relationships
takes upwards of 20 joins. Yet the database only has 4 tables...

