Option type #
We introduce an important generic sum type, available in many languages, under a variety of names (Maybe, Option, optional, Optional, Nullable, etc.)
We will see in the dedicated sections that such a type can also be made a functor, and (perhaps most importantly) a monad. For now, we will use it without these extra features.
Motivation #
Problem. A programming language may not have a type that exactly fits the domain of a function that we want to implement.
Examples. A programming language may not have a type that exactly fits:
- positive integers,
- integers $\neq 0$,
- ASCII characters,
- nonempty lists (or nonempty arrays, nonempty sets, etc.),
- lists of distinct elements,
- valid phone numbers,
- etc.
Consequence. When we implement a function, we may need to handle undesired inputs.
Examples.
- A function that returns the head of a list may receive the empty list as input,
- a function that divides two numbers may receive
0as input divisor,- a function that maps a date to the average temperature in Bolzano on that day may receive a future date as input,
- etc.
null value and propagation
#
A common way for a function $f$ to handle non-supported inputs consists in returning a special value.
Depending on the language, this value may be called null, nil, undefined, etc.
Consequence. A function that calls $f$ must take this possibility into consideration:
- if $f$ returns
null, take a special action (e.g. returnnullas well),- otherwise proceed normally.
Drawback. Programmers often forget to check whether the value returned by $f$ is
null. This is a well-known source of bugs (e.g. “null pointer exception”).
Illustration. Tony Hoare is often credited for inventing the
nullvalue (when working on the programming language ALGOL W). He notoriously said (in 2009):“I call it my billion-dollar mistake. It was the invention of the null reference in 1965. […] I couldn’t resist the temptation to put in a null reference, […] 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.”
Second drawback. Handling
nullvalues creates boilerplate code (made worse by the fact thatnullis often propagated).
For instance (in Java):Integer myMethod1 (Integer x){ if(x == 0){ return null; } else { ... } String myMethod2 (Integer y){ if(y < 0){ return null; } else { x = myMethod1(myMethod3(y)); if(x == null){ return null; } else { ... } } }
Option type #
Functional languages typically do not have a
nullvalue. Instead, they rely on one of several special types for optional values.These are often called option types or maybe types.
Benefits. In a
null-free language with option types:
The return type of a function indicates whether it may not return a value. This reduces:
- the odds of a non-handled missing value, and
- unnecessary checks.
Option types can be paired with higher-order functions that propagate “missing values” automatically (thus reducing boilerplate code). We will introduce such functions in the sections on functors and monads.
In Haskell #
Haskell uses a generic option type called Maybe.
Declaration. The type
Maybeis declared asdata Maybe a = Nothing | Just aIntuitively, an expression with type
Maybe amay evaluate to either nothing, or a value of typea(whereais a type variable).
Example. The following function maps a nonempty list with head
xtoJust x, and the empty list toNothing.maybeHead :: [a] -> Maybe a maybeHead [] = Nothing maybeHead (x : _) = Just x
Implement the function
maybeLast :: [a] -> Maybe a
that maps a nonempty list to its last element, and the empty list to Nothing.
maybeLast :: [a] -> Maybe a
maybeLast [] = Nothing
maybeLast [x] = Just x
maybeLast (_ : xs) = maybeLast xs
maybeLast :: [a] -> Maybe a
maybeLast = maybeHead . reverse
Implement the native function
find :: (a -> Bool) -> [a] -> Maybe a
so that find f xs evaluates to:
- the first element of
xsfor whichfevaluates toTrue, and Nothingif there is no such element.
find :: (a -> Bool) -> [a] -> Maybe a
find _ [] = Nothing
find f (x : xs) | f x = Just x
| otherwise = find f xs