Chapter 6 Dealing with Optional Data
This chapter covers the null reference or the billion dollar mistake, alternatives to
null references, developing an option data type for optional data, applying functions
to optional values, composing optional values.
Representing optional data has always been a problem in computer programs.
The concept of optional data is simple.
In everyday life, it's easy to represent the absence of something when this something is
stored in a container.
Whatever it is, it can be represented by an empty container.
For example, an absence of apples can be represented by an empty apple basket.
The absence of gasoline in a car can be visualized as an empty gas tank.
But representing the absence of data in computer programs is a little more difficult.
Most data is represented by a reference pointing to it, so the most obvious way to represent
the absence of data is to use a pointer to nothing.
That's what we call a null pointer.
In Kotlin, a reference is a pointer to a value.
In most programming languages, references can be changed to point to a new value.
For this reason, the term variable is often used as a replacement for reference.
The word variable is not much of a problem when all references can be later reassigned
to a new value.
In such cases, it's normal to use the term variable as a replacement for reference.
But that can be different when you use references that can't be reassigned.
In such a case, you end up with two types of variables, those that can vary and those
that cannot.
It's then safer to use the term reference.
Some references can be created null and then changed to point to values.
They can even be changed again to point to null if data is removed.
In such cases, it's safe to call them variables, meaning variable references.
Conversely, some references can't be created without making them point to a value, and
once this is done, they can't be made to point to a different value.
Such references are sometimes called constants.
In Java, for example, references are generally variables unless declared final, which makes
them constants.
In Kotlin, variable references are declared with the keyword var and non-variable ones,
which are also called immutable references, are declared as val.
At first, you may think that optional data might only be referenced with var.
What would be the point of creating a reference to an absence of data if not to eventually
change this to a reference to some data?
But how would you represent the absence of data?
The most common way is to use what's called a null reference.
A null reference is a reference not pointing to anything until some data is assigned to it.
In this chapter, you'll learn how to deal with optional data, absent values that aren't
the result of an error.
I start by discussing problems with the null pointer.
After that, I discuss how Kotlin handles null references and Kotlin's alternatives to null references.
For the remainder of the chapter, I describe how to create and use the option type as a
solution for working with optional data.
You can use the option type to compose functions even when the data is absent.
Section 6.1.
Problems with the null pointer.
One of the most frequent bugs in programs that allow null references is the null pointer exception.
This exception is raised when an identifier is dereferenced and found to be pointing to nothing.
Some data is expected but is found to be missing.
Such an identifier is said to be pointing to null.
In this section, you'll learn about the problems with using null references.
In 1965, when designing ALGOL, an object-oriented language, Tony Hoare invented the null reference.
He later came to regret it.
Here's what he said about it in 2009.
I call it my billion-dollar mistake.
My goal was to ensure that all use of references should be absolutely safe, with checking performed
automatically by the compiler.
But I couldn't resist the temptation to put in a null reference because it was so easy
to implement.
This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused
a billion dollars of pain and damage in the last 40 years.
Although by now it should be common knowledge to avoid the null reference, that's far from
being the case.
The Java Standard Library, which is also used in Kotlin programs, contains methods and constructors
taking optional parameters that must be set to null if the parameters are absent.
Take, for example, the java.net.socket class.
This class defines the following constructor.
See this code?
According to the documentation, if the specified local address is null, it is the equivalent
of specifying the address as the local address.
Here the null reference is a valid parameter.
This is sometimes called a business null.
This way of handling the absence of data isn't specific to objects.
The port can also be absent, but in Java it can't be null because it's a primitive.
A local port number of 0 will let the system pick up a free port in the bind operation.
This kind of value is sometimes called a sentinel value.
It's not used for the value itself.
It doesn't mean port 0, but it's used to specify the absence of a port indication.
You can find many other examples of handling the absence of data in the Java Standard Library.
The use of null to represent absence of data is dangerous because the fact that the local
address is null could be unintentional and due to a former error.
But this use won't cause an exception.
The program will continue working, although not as intended.
There are other cases of business nulls.
If you try to retrieve a value from a hash map using a key that's not in the map, you'll
get a null.
Is this an error?
You don't know.
It might be that the key is valid but hasn't been registered in the map, or it might be
that the key is supposedly valid and should be in the map, but there was a previous error
while computing the key.
For example, the key could be null, whether intentionally or due to an error, and this
won't raise an exception.
It could even return a non-null value because the null key is allowed in a hash map.
This situation is a complete mess.
As a professional programmer, you know what to do about this.
You know that you should never use a reference without checking whether it's null or not.
You do this for each object parameter received by a function, right?
You know that you should never get a value from a map without first testing whether the
map contains the corresponding key.
You know that you should never try to get an element from a list without verifying first
that the list isn't empty and that it has enough elements if you're accessing the element
through its index.
If you do either of these, you never get a null pointer exception or an index out-of-bounds
exception, and you can live with null references without any specific protection.
But if you happen to forget and sometimes get these kinds of nasty exceptions, you might
want an easier and safer way of dealing with the absence of a value, whether intentional
or resulting from an error.
In this chapter, you'll learn how to deal with absent values that aren't the result
of an error.
This kind of data is called optional data.
There are always tricks for dealing with optional data.
One of the best known and most often used is the list.
When a function is supposed to return either a value or nothing, some programmers use the
list as the return value.
The list can contain zero or one element.
Although this works quite well, it has several important drawbacks.
There's no way to ensure that the list contains at most one element.
What should you do if you receive a list of several elements?
How can you distinguish between a list that's supposed to hold at most one element and a
regular list?
The list class defines many functions to deal with the fact that lists can contain several
elements.
These functions are useless for your use case.
Lists are complex structures, and you don't need this.
A much simpler implementation is sufficient.