True, False, ...Maybe?!

cs 19-03-2024

Among the cornerstones of computer science is the idea of boolean logic: true or false, 1 or 0, yes or no, on or off, and so on. Almost every programming language has some analogue to this, implemented in varying ways and with different syntax.

What is lesser known is that it is also possible to have logic with 3 values: often called three-valued logic, or trilean logic.

This logic preserves the true and false states, but also adds a third state: often explained as “unknown,” or, “maybe.” This post was inspired by me stumbling upon Haskell’s Maybe type, and I thought it was interesting enough to do some further investigation.

In the Haskell documentation, Maybe was defined to be the following:

data Maybe a = Nothing | Just a
  deriving (Eq, Ord)

As you can see, Maybe is used when it is possible to have nothing, or something. But why, you might ask, is this useful? Couldn’t we just use a null pointer?

Why null sucks

First, let’s see what Tony Hoare, the inventor of null, has to say:

I call it my billion-dollar mistake. At that time, I was designing the first comprehensive type system for references in an object-oriented language. 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, simply 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 forty years.

A lot more info is provided in his 2009 presentation, Null References: The Billion Dollar Mistake , however the gist is here. The ease in which programmers can make mistakes while using null, especially if they believe a value is not null, is extensive and has been proven time and time again with security vulnerabilities throughout the ages.

However, the fundamental idea null is trying to convey is often useful: the idea that a value may not be there. Rust, in particular, solves this in an interesting way: with the Option<> enum, which is basically an analogue to Haskell’s Maybe:

enum Option<T> {
    None,
    Some(T),
}

Here, Option<> can either be None, or Some value. Therefore, instead of having to checking whether any variable is null or non-null, we can simply express this logic by wrapping our values in Option<>: which means that now, compilers can help us to avoid any null pointer exceptions that have caused so much pain and suffering in this world.

An implementation

Python is an example of a language in which null is used: their None. Here, we can try to avoid that by creating a helper class that can implement some of this Maybe logic we’ve been talking about:

class Option:
    def __init__(self, value=None):
        self._value = value

    def is_some(self):
        return self._value is not None

    def is_none(self):
        return self._value is None

    def unwrap(self):
        if self.is_some():
            return self._value
        else:
            raise ValueError("Option is None")

    def unwrap_or(self, default_value):
        return self._value if self.is_some() else default_value

Here, we have the option of unwrap_or — and we provide a way for compilers to check whether or not our code logic has the potential to create null pointer exceptions.

Author's photo

Lin Jiang

I’m a high school student that's passionate about computer science. Join me in having fun through recreational programming!

See other articles: