Maybe
is used to represent possibly empty values - similar to null
in other languages. Usually it is used as the output type of functions that can fail in some way.
Consider the following function:
halve :: Int -> Maybe Int
halve x
| even x = Just (x `div` 2)
| odd x = Nothing
Think of halve
as an action, depending on an Int
, that tries to halve the integer, failing if it is odd.
How do we halve
an integer three times?
takeOneEighth :: Int -> Maybe Int -- (after you read the 'do' sub-section:)
takeOneEighth x =
case halve x of -- do {
Nothing -> Nothing
Just oneHalf -> -- oneHalf <- halve x
case halve oneHalf of
Nothing -> Nothing
Just oneQuarter -> -- oneQuarter <- halve oneHalf
case halve oneQuarter of
Nothing -> Nothing -- oneEighth <- halve oneQuarter
Just oneEighth ->
Just oneEighth -- return oneEighth }
takeOneEighth
is a sequence of three halve
steps chained together.halve
step fails, we want the whole composition takeOneEighth
to fail.halve
step succeeds, we want to pipe its result forward.instance Monad Maybe where
-- (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing >>= f = Nothing -- infixl 1 >>=
Just x >>= f = Just (f x) -- also, f =<< m = m >>= f
-- return :: a -> Maybe a
return x = Just x
and now we can write:
takeOneEighth :: Int -> Maybe Int
takeOneEighth x = halve x >>= halve >>= halve -- or,
-- return x >>= halve >>= halve >>= halve -- which is parsed as
-- (((return x) >>= halve) >>= halve) >>= halve -- which can also be written as
-- (halve =<<) . (halve =<<) . (halve =<<) $ return x -- or, equivalently, as
-- halve <=< halve <=< halve $ x
Kleisli composition <=<
is defined as (g <=< f) x = g =<< f x
, or equivalently as (f >=> g) x = f x >>= g
. With it the above definition becomes just
takeOneEighth :: Int -> Maybe Int
takeOneEighth = halve <=< halve <=< halve -- infixr 1 <=<
-- or, equivalently,
-- halve >=> halve >=> halve -- infixr 1 >=>
There are three monad laws that should be obeyed by every monad, that is every type which is an instance of the Monad
typeclass:
1. return x >>= f = f x
2. m >>= return = m
3. (m >>= g) >>= h = m >>= (\y -> g y >>= h)
where m
is a monad, f
has type a -> m b
and g
has type b -> m c
.
Or equivalently, using the >=>
Kleisli composition operator defined above:
1. return >=> g = g -- do { y <- return x ; g y } == g x
2. f >=> return = f -- do { y <- f x ; return y } == f x
3. (f >=> g) >=> h = f >=> (g >=> h) -- do { z <- do { y <- f x; g y } ; h z }
-- == do { y <- f x ; do { z <- g y; h z } }
Obeying these laws makes it a lot easier to reason about the monad, because it guarantees that using monadic functions and composing them behaves in a reasonable way, similar to other monads.
Let's check if the Maybe
monad obeys the three monad laws.
return x >>= f = f x
return z >>= f
= (Just z) >>= f
= f z
m >>= return = m
Just
data constructorJust z >>= return
= return z
= Just z
Nothing
data constructorNothing >>= return
= Nothing
(m >>= f) >>= g = m >>= (\x -> f x >>= g)
Just
data constructor-- Left-hand side
((Just z) >>= f) >>= g
= f z >>= g
-- Right-hand side
(Just z) >>= (\x -> f x >>= g)
(\x -> f x >>= g) z
= f z >>= g
Nothing
data constructor-- Left-hand side
(Nothing >>= f) >>= g
= Nothing >>= g
= Nothing
-- Right-hand side
Nothing >>= (\x -> f x >>= g)
= Nothing