I am just starting to program in Haskell, and I came across the following definition:
calculate :: Float -> Float -> Maybe Float
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).