Null Values
Earlier I complained about the fact that function parameters are never constant by default. I do not know of any non-functional language that hasn’t made this mistake (this of course not being an issue in a functional language since everything is a value — immutable.)
Another common decision that I think is even worse, and might be the worst feature of these languages is null. null is treated as an object by the compilers/interpreters, but the programmer can not treat it as such.
This means that anywhere you can put an object, you may also put null. Car myCar = null; is perfectly valid Java, and even myCar.getManufacturer(); will compile. You will then have to wait until the actual code runs in order to get a NullPointerException. What this means in practice is that any time you’re not 100% sure that a value cannot possibly be null, you have to perform a myCar == null check to avoid these errors. Of course, you’ll want to avoid null values as often as possible for this reason. There is also the notion of Null Object in OOP languages, an object that also means “no value”, but it’s a real object meaning myCar.getManufacturer(); will in fact work, albeit it might return “N/A” instead, a procedure will simply do nothing when operating on null objects.
It quickly becomes hard to know where you will need these null checks and where null objects are available. Can you really trust documentation to give you all the information you need? If you assume that a value can’t be null because the docs don’t say they can, you will probably run into trouble. If you assume they can be null, you will have to write so many null checks your hands will call it quits.
Objective-C has an interesting (albeit in my opinion terrible) way of
treating null values; pass a message to a null value, and no method
executes, null is simply returned again. This allows for chains like
[[[[a b] c] d] e] returning null, instead of operations
being performed. Then you of course have to figure out where in the
chain the null value popped up.
Haskell has solved this in what i find to be an elegant manner. For
instance, the function lookup (that tries to find a certain value in
an association list) has the type signature
Eq a => a -> [(a, b)] -> Maybe b.
If you do not know Haskell, this type signature can be explained as “A
function taking a key a to look up in an association list
[(a, b)] (a list of key/value pairs) and then returns a
value of type Maybe b where b is the value
of such a list. An association list in Haskell is not a distinct data
type, it’s just by convention a list of tuples, but that’s another subject.
Maybe b is a type that has two constructors, either
Just b or Nothing. Nothing means that no
value exists, or in this instance that no key/value pair with the
given key existed. Just b means that a value b exists (was found).
The alternative in other languages would be to either return null when no value was found, or to throw an exception.
I recently found the reason that null came into existence, first invented and implemented by Tony Hoare (Inventor of QuickSort, Turing Award winner) back in 1965 for the OO language Algol W. The reason it was added? It was easy to implement! Tony Hoare has realized how huge of a mistake this was.
Unfortunately designers of new languages seem to mostly mimic what’s already been done. Sure, changing something everybody expects (the ability to specify values as null) can be a hit or miss in the software engineering community where, theoretical upsides are seldom a deciding factor. My software engineering professor stated this fact quite bluntly by saying “In SE; if it seems to work, we use it.”
Post new comment