What is the Maybe type and how does it work?

K... picture K... · Apr 5, 2015 · Viewed 9.5k times · Source

I am just starting to program in Haskell, and I came across the following definition:

calculate :: Float -> Float -> Maybe Float

Answer

Willem Van Onsem picture Willem Van Onsem · Apr 5, 2015

Maybe a is an ordinary data type defined as:

data Maybe a = Just a | Nothing

There are thus two possibilities: or you define a value of type a as Just a (like Just 3), or Nothing in case the query has no answer.

It is meant to defined as a way to define output for non-total functions.

For instance: say you want to define sqrt. The square root is only defined for positive integers, you can thus define sqrt as:

sqrt x | x >= 0 = Just $ ...
       | otherwise = Nothing

with ... a way to calculate the square root for x.

Some people compare Nothing with the "null pointer" you find in most programming languages. By default, you don't implement a null pointer for data types you define (and if you do, all these "nulls" look different), by adding Nothing you have a generic null pointer.

It can thus be useful to use Maybe to denote that it is possible no output can be calculated. You could of course also error on values less than 0:

sqrt x | x >= 0 = Just $ ...
       | otherwise = error "The value must be larger or equal to 0"

But errors are normally not mentioned in the type signature, nor does a compiler have any problem if you don't take them into account. Haskell is also shifting to total functions: it's better to alway try at least to return a value (e.g. Nothing) for all possible inputs.

If you later want to use the result of a Maybe a, you for instance need to write:

succMaybe :: Maybe Int -> Maybe Int
succMaybe (Just x) = Just (x+1)
succMaybe _ = Nothing

But by writing Just for the first case, you somehow warn yourself that it is possible Nothing can occur. You can also get rid of the Maybe by introducing a "default" value:

justOrDefault :: a -> Maybe a -> a
justOrDefault _ (Just x) = x
justOrDefault d _ = d

The builtin maybe function (note the lowercase), combines the two previous functions:

maybe :: b -> (a -> b) -> Maybe a -> b
maybe _ f (Just x) = f x
maybe z _ Nothing  = z

So you specify a b (default value) together with a function (a -> b). In case Maybe a is Just x, the function is applied to it and returned, in case the input value is Nothing, the default value will be used.

Working with Maybe a's can be hard, because you always need to take the Nothing case into account, to simplify this you can use the Maybe monad.


Tom Schrijvers also shows that Maybe is the successor function in type algebra: you add one extra value to your type (Either is an addition and (,) is the type-algebraic equivalent of multiplication).