Part 16

The Maybe type

In addition to the list type, Haskell has other parameterized types too. Let’s look at a very common and useful one: the Maybe type.

Sometimes an operation doesn’t have a valid return value (E.g. division by zero.). We have a couple of options in this situation. We can use an error value, like -1. This is a bit ugly, not always possible. We can throw an exception. This is impure. In some other languages we would return a special null value that exists in (almost) all types. However Haskell does not have a null.

The solution Haskell offers us instead is to change our return type to a Maybe type. This is pure, safe and neat. The type Maybe a has two constructors: Nothing and Just. Nothing is just a constant, but Just takes a parameter. More concretely:

TypeValues
Maybe BoolNothing, Just False, Just True
Maybe IntNothing, Just 0, Just 1, …
Maybe [Int]Nothing, Just [], Just [1,1337], …

You can think of Maybe a as being a bit like [a] except there can only be 0 or 1 elements, not more. Alternatively, you can think of Maybe a introducing a null value to the type a. If you’re familiar with Java, Maybe Integer is the Haskell equivalent of Java’s Optional<Integer>.

You can create Maybe values by either specifying Nothing or Just someOtherValue:

Prelude> :t Nothing
Nothing :: Maybe a
Prelude> Just "a camel"
Just "a camel"
Prelude> :t Just "a camel"
Just "a camel" :: Maybe [Char]   -- the same as Maybe String
Prelude> Just True
Just True
Prelude> :t Just True
Just True :: Maybe Bool

-- given a password, return (Just username) if login succeeds, Nothing otherwise
login :: String -> Maybe String
login "f4bulous!" = Just "unicorn73"
login "swordfish" = Just "megahacker"
login _           = Nothing

You use a Maybe value by pattern matching on it. Usually you define patterns for the Nothing and Just something cases. Some examples:

-- Multiply an Int with a Maybe Int. Nothing is treated as no multiplication at all.
perhapsMultiply :: Int -> Maybe Int -> Int
perhapsMultiply i Nothing = i
perhapsMultiply i (Just j) = i*j   -- Note how j denotes the value inside the Just

Prelude> perhapsMultiply 3 Nothing
3
Prelude> perhapsMultiply 3 (Just 2)
6

intOrZero :: Maybe Int -> Int
intOrZero Nothing = 0
intOrZero (Just i) = i

safeHead :: [a] -> Maybe a
safeHead xs = if null xs then Nothing else Just (head xs)

headOrZero :: [Int] -> Int
headOrZero xs = intOrZero (safeHead xs)

headOrZero []  ==> intOrZero (safeHead [])  ==> intOrZero Nothing  ==> 0
headOrZero [1] ==> intOrZero (safeHead [1]) ==> intOrZero (Just 1) ==> 1

Sidenote: Constructors

As you can see above, we can pattern match on the constructors of Maybe: Just and Nothing. We’ll get back to what constructors mean later. For now it’s enough to note that constructors are special values that start with a capital letter that you can pattern match on.

Other constructors that we’ve already seen include the constructors of BoolTrue and False. We’ll introduce the constructors of the list type on the next lecture.

Constructors can be used just like Haskell values. Constructors that take no arguments like Nothing, and False are just constants. Constructors like Just that take an argument behave like functions. They even have function types!

Prelude> :t Just
Just :: a -> Maybe a

Exercises:

All exercises can be found in Set2a and Set2b. Please pay attention in the title of the exercise in which file the exercises of this section can be found.

Exercises from 2a:

You have reached the end of this section! Continue to the next section:

You can check your current points from the blue blob in the bottom-right corner of the page.