Skip to main content
4 of 4
in a nutshell: nullable types break polymorphism

Much of the angst over nulls are due to languages where every reference type is nullable by default. But this is not an issue for Typescript or C# 8 so lets disregard that.

There are two basic approaches to how to optional values are represented:

  • A distinct container type (the Option type) which contain zero or one instances of the actual value.

  • A union type of the original type with a "sentinel" value, null, which indicate the lack of a value. In Typescript it would be declared as a (actualType | null) type union. (Or shortened as actualType?, but the the explicit union makes it clearer what is going on.)

On the face of it they seem similar, but a significant difference is that containers nest but type unions don't. An option can contain another option as its value, but ((actualType | null) | null) is just the same as (actualType | null).

For example consider a dictionary. The lookup function could return an option: Nothing if the key does not exist, otherwise Some value. This will work with any type of value. If the values in the dictionary are themselves options, the lookup will just return an option where the value (if any) is itself an option.

But what if we instead use null to represent a missing value? Then the lookup function can return a value or a null, but there is no way to distinguish between if the null means the key did not exist or the key did exist but the associated value was null. You lose what could be important information.

Dictionaries are just an example, the problem arise anywhere you have a data-structure with multiple levels of optional elements. Nullable types prevent polymorphism: code can't manipulate data of an unknown type generically, it has to treat nullable and non-nullable types differently.

You can see how C# has to use awkward workarounds in the Dictionary interface. Either it throws an Exception (eww!) if the key is not found, or with TryGetValue() it returns a boolean indicating if the key is found and then the value (or null) on an out parameter. This is pretty ugly and it doesn't scale and is not composable. Option types solves this elegantly, and in a way that is consistent with the rest of the language.

JacquesB
  • 61k
  • 21
  • 133
  • 187