Haskell Language Monads The Maybe monad


Example

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.
  • If a halve step fails, we want the whole composition takeOneEighth to fail.
  • If a 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.

  1. The left identity law - return x >>= f = f x
return z >>= f 
= (Just z) >>= f 
= f z
  1. The right identity law - m >>= return = m
  • Just data constructor
Just z >>= return
= return z
= Just z  
  • Nothing data constructor
Nothing >>= return
= Nothing 
  1. The associativity law - (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