Actually, null is a great idea. Given a pointer, we want to designate that this pointer does not reference a valid value. So we take one memory location, declare it invalid, and stick to that convention (a convention sometimes enforced with segfaults). Now whenever I have a pointer I can check if it contains Nothing (ptr == null) or Some(value) (ptr != null, value = *ptr). I want you to understand that this is equivalent to a Maybe type.
The problems with this are:
In many language the type system does not assist here to guarantee a non-null reference.
This is historical baggage, as many mainstream imperative or OOP languages have only had incremental advances in their type systems when compared to predecessors. Small changes have the advantage that new languages are easier to learn. C# is taking steps away from null referencesa mainstream language that has introduced language-level tools to better handle nulls.
API designers might return
nullon failure, but not a reference to the actual thing itself on success. Often, the thing (without a reference) is returned directly. This flattening of one pointer level makes it impossible to usenullas a value.This is just laziness on the designer's side and can't be helped without enforcing proper nesting with a proper type system. Some people might also try to justify this with performance considerations, or with the existence of optional checks (a collection might return
nullor the item itself, but also provide ancontainsmethod).In Haskell there is a neat view onto the
Maybetype as a monad. This makes it easier to compose transformations on the contained value.On the other hand, low-level languages like C barely treat arrays as a separate type, so I'm not sure what we're expecting. In OOP languages with parameterized polymorphism, a runtime-checked
Maybetype is rather trivial to implement.