Prev: Exercises TOC: Contents Next: Part II - Introduction

Monad support in Haskell


Haskell's built in support for monads is split among the standard prelude, which exports the most common monad functions, and the Monad module, which contains less-commonly used monad functions. The individual monad types are each in their own libraries and are the subject of Part II of this tutorial.

In the standard prelude

The Haskell 98 standard prelude includes the definition of the Monad class as well as a few auxilliary functions for working with monadic data types.

The Monad class

We have seen the Monad class before:
class  Monad m  where
    (>>=)  :: m a -> (a -> m b) -> m b
    (>>)   :: m a -> m b -> m b
    return :: a -> m a
    fail   :: String -> m a

        -- Minimal complete definition:
        --      (>>=), return
    m >> k  =  m >>= \_ -> k
    fail s  = error s

The sequencing functions

The sequence function takes a list of monadic computations, executes each one in turn and returns a list of the results. If any of the computations fail, then the whole function fails:
sequence :: Monad m => [m a] -> m [a] 
sequence = foldr mcons (return [])
             where mcons p q = p >>= \x -> q >>= \y -> return (x:y)

The sequence_ function (notice the underscore) has the same behavior as sequence but does not return a list of results. It is useful when only the side-effects of the monadic computations are important.
sequence_ :: Monad m => [m a] -> m () 
sequence_ = foldr (>>) (return ())

The mapping functions

The mapM function maps a monadic computation over a list of values and returns a list of the results. It is defined in terms of the list map function and the sequence function above:
mapM :: Monad m => (a -> m b) -> [a] -> m [b] 
mapM f as = sequence (map f as)

There is also a version with an underscore, mapM_ which is defined using sequence_. mapM_ operates the same as mapM, but it doesn't return the list of values. It is useful when only the side-effects of the monadic computation are important.
mapM_ :: Monad m => (a -> m b) -> [a] -> m () 
mapM_ f as = sequence_ (map f as)

As a simple example of the use the mapping functions, a putString function for the IO monad could be defined as:
putString :: [Char] -> IO ()
putString s = mapM_ putChar s

mapM can be used within a do block in a manner similar to the way the map function is normally used on lists. This is a common pattern with monads — a version of a function for use within a monad (i.e., intended for binding) will have a signature similar to the non-monadic version but the function outputs will be within the monad:
-- compare the non-monadic and monadic signatures
map  ::            (a -> b)   -> [a] -> [b]
mapM :: Monad m => (a -> m b) -> [a] -> m [b] 

The reverse binder function (=<<)

The prelude also defines a binding function that takes it arguments in the opposite order to the standard binding function. Since the standard binding function is called ">>=", the reverse binding function is called "=<<". It is useful in circumstances where the binding operator is used as a higher-order term and it is more convenient to have the arguments in the reversed order. Its definition is simply:
(=<<) :: Monad m => (a -> m b) -> m a -> m b
f =<< x = x >>= f

In the Monad module

The Monad module in the standard Haskell 98 libraries exports a number of facilities for more advanced monadic operations. To access these facilities, simply import Monad in your Haskell program.

Not all of the function in the Monad module are discussed here, but you are encouraged to explore the module for yourself when you feel you are ready to see some of the more esoteric monad functions.

The MonadPlus class

The Monad module defines the MonadPlus class for monads with a zero element and a plus operator:
class Monad m => MonadPlus m where
    mzero :: m a
    mplus :: m a -> m a -> m a

Monadic versions of list functions

Several functions are provided which generalize standard list-processing functions to monads. The mapM functions are exported in the standard prelude and were described above.

foldM is a monadic version of foldl in which monadic computations built from a list are bound left-to-right. The definition is:
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM f a []     = return a
foldM f a (x:xs) = f a x >>= \y -> foldM f y xs
but it is easier to understand the operation of foldM if you consider its effect in terms of a do block:
-- this is not valid Haskell code, it is just for illustration
foldM f a1 [x1,x2,...,xn] = do a2 <- f a1 x1
                               a3 <- f a2 x2
                               ...
                               f an xn
Right-to-left binding is achieved by reversing the input list before calling foldM.

We can use foldM to create a more poweful query function in our sheep cloning example:
Code available in example3.hs
-- traceFamily is a generic function to find an ancestor
traceFamily :: Sheep -> [ (Sheep -> Maybe Sheep) ] -> Maybe Sheep
traceFamily s l = foldM getParent s l
  where getParent s f = f s

-- we can define complex queries using traceFamily in an easy, clear way
mothersPaternalGrandfather s = traceFamily s [mother, father, father]
paternalGrandmother s = traceFamily s [father, mother]
The traceFamily function uses foldM to create a simple way to trace back in the family tree to any depth and in any pattern. In fact, it is probably clearer to write "traceFamily s [father, mother]" than it is to use the paternalGrandmother function!

A more typical use of foldM is within a do block:
Code available in example4.hs
-- a Dict is just a finite map from strings to strings
type Dict = FiniteMap String String

-- this an auxilliary function used with foldl
addEntry :: Dict -> Entry -> Dict
addEntry d e = addToFM d (key e) (value e)

-- this is an auxiliiary function used with foldM inside the IO monad
addDataFromFile :: Dict -> Handle -> IO Dict
addDataFromFile dict hdl = do contents <- hGetContents hdl
                              entries  <- return (map read (lines contents))
                              return (foldl (addEntry) dict entries)

-- this program builds a dictionary from the entries in all files named on the
-- command line and then prints it out as an association list
main :: IO ()
main = do files   <- getArgs
          handles <- mapM openForReading files
          dict    <- foldM addDataFromFile emptyFM handles
          print (fmToList dict)

The filterM function works like the list filter function inside of a monad. It takes a predicate function which returns a Boolean value in the monad and a list of values. It returns, inside the monad, a list of those values for which the predicate was True.
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
filterM p []     = return []
filterM p (x:xs) = do b  <- p x
                      ys <- filterM p xs
                      return (if b then (x:ys) else ys)

Here is an example showing how filterM can be used within the IO monad to select only the directories from a list:
Code available in example5.hs
import Monad
import Directory
import System

-- NOTE: doesDirectoryExist has type FilePath -> IO Bool

-- this program prints only the directories named on the command line
main :: IO ()
main = do names <- getArgs
          dirs  <- filterM doesDirectoryExist names
          mapM_ putStrLn dirs

zipWithM is a monadic version of the zipWith function on lists. zipWithM_ behaves the same but discards the output of the function. It is useful when only the side-effects of the monadic computation matter.
zipWithM ::(Monad m) => (a -> b -> m c) -> [a] -> [b] -> m [c]
zipWithM f xs ys = sequence (zipWith f xs ys)

zipWithM_ ::(Monad m) => (a -> b -> m c) -> [a] -> [b] -> m ()
zipWithM_ f xs ys = sequence_ (zipWith f xs ys)

Conditional monadic computations

There are two functions provided for conditionally executing monadic computations. The when function takes a boolean argument and a monadic computation with unit "()" type and performs the computation only when the boolean argument is True. The unless function does the same, except that it performs the computation unless the boolean argument is True.
when :: (Monad m) => Bool -> m () -> m ()
when p s = if p then s else return ()

unless :: (Monad m) => Bool -> m () -> m ()
unless p s = when (not p) s

ap and the lifting functions

Lifting is a monadic operation that converts a non-monadic function into an equivalent function that operates on monadic values. We say that a function is "lifted into the monad" by the lifting operators. A lifted function is useful for operating on monad values outside of a do block and can also allow for cleaner code within a do block.

The simplest lifting operator is liftM, which lifts a function of a single argument into a monad.
liftM :: (Monad m) => (a -> b) -> (m a -> m b)
liftM f = \a -> do { a' <- a; return (f a') }

Lifting operators are also provided for functions with more arguments. liftM2 lifts functions of two arguments:
liftM2 :: (Monad m) => (a -> b -> c) -> (m a -> m b -> m c)
liftM2 f = \a b -> do { a' <- a; b' <- b; return (f a' b') }
The same pattern is applied to give the definitions to lift functions of more arguments. Functions up to liftM5 are defined in the Monad module.

To see how the lifting operators allow more concise code, consider a computation in the Maybe monad in which you want to use a function swapNames::String -> String. You could do:
getName :: String -> Maybe String
getName name = do let db = [("John", "Smith, John"), ("Mike", "Caine, Michael")]
                  tempName <- lookup name db
                  return (swapNames tempName)
But making use of the liftM function, we can use liftM swapNames as a function of type Maybe String -> Maybe String:
Code available in example6.hs
getName :: String -> Maybe String
getName name = do let db = [("John", "Smith, John"), ("Mike", "Caine, Michael")]
                  liftM swapNames (lookup name db)
The difference is even greater when lifting functions with more arguments.

The lifting functions also enable very concise constructions using higher-order functions. To understand this example code, you might need to review the definition of the monad functions for the List monad (particularly >>=). Imagine how you might implement this function without lifting the operator:
Code available in example7.hs
-- allCombinations returns a list containing the result of
-- folding the binary operator through all combinations
-- of elements of the given lists
-- For example, allCombinations (+) [[0,1],[1,2,3]] would be
--   [0+1,0+2,0+3,1+1,1+2,1+3], or [1,2,3,2,3,4]
-- and allCombinations (*) [[0,1],[1,2],[3,5]] would be
--   [0*1*3,0*1*5,0*2*3,0*2*5,1*1*3,1*1*5,1*2*3,1*2*5], or [0,0,0,0,3,5,6,10]
allCombinations :: (a -> a -> a) -> [[a]] -> [a]
allCombinations fn []     = []
allCombinations fn (l:ls) = foldl (liftM2 fn) l ls

There is a related function called ap that is sometimes more convenient to use than the lifting functions. ap is simply the function application operator ($) lifted into the monad:
ap :: (Monad m) => m (a -> b) -> m a -> m b
ap = liftM2 ($)
Note that liftM2 f x y is equivalent to return f `ap` x `ap` y, and so on for functions of more arguments. ap is useful when working with higher-order functions and monads.

The effect of ap depends on the strategy of the monad in which it is used. So for example [(*2),(+3)] `ap` [0,1,2] is equal to [0,2,4,3,4,5] and (Just (*2)) `ap` (Just 3) is Just 6. Here is a simple example that shows how ap can be useful when doing higher-order computations:
Code available in example8.hs
-- lookup the commands and fold ap into the command list to
-- compute a result.
main :: IO ()
main = do let fns  = [("double",(2*)),      ("halve",(`div`2)),
                      ("square",(\x->x*x)), ("negate", negate),
                      ("incr",(+1)),        ("decr",(+(-1)))
                     ]
          args <- getArgs
          let val  = read (args!!0)
              cmds = map ((flip lookup) fns) (words (args!!1))
          print $ foldl (flip ap) (Just val) cmds

Functions for use with MonadPlus

There are two functions in the Monad module that are used with monads that have a zero and a plus. The first function is msum, which is analogous to the sum function on lists of integers. msum operates on lists of monadic values and folds the mplus operator into the list using the mzero element as the initial value:
msum :: MonadPlus m => [m a] -> m a
msum xs = foldr mplus mzero xs

In the List monad, msum is equivalent to concat. In the Maybe monad, msum returns the first non-Nothing value from a list. Likewise, the behavior in other monads will depend on the exact nature of their mzero and mplus definitions.

msum allows many recursive functions and folds to be expressed more concisely. In the Maybe monad, for example, we can write:
Code available in example9.hs
type Variable = String
type Value = String
type EnvironmentStack = [[(Variable,Value)]]

-- lookupVar retrieves a variable's value from the environment stack
-- It uses msum in the Maybe monad to return the first non-Nothing value.
lookupVar :: Variable -> EnvironmentStack -> Maybe Value
lookupVar var stack = msum $ map (lookup var) stack
instead of:
lookupVar :: Variable -> EnvironmentStack -> Maybe Value
lookupVar var []     = Nothing
lookupVar var (e:es) = let val = lookup var e
                       in maybe (lookupVar var es) Just val

The second function for use with monads with a zero and a plus is the guard function:
guard :: MonadPlus m => Bool -> m ()
guard p = if p then return () else mzero
The trick to understanding this function is to recall the law for monads with zero and plus that states mzero >>= f == mzero. So, placing a guard function in a sequence of monadic operations will force any execution in which the guard is False to be mzero. This is similar to the way that guard predicates in a list comprehension cause values that fail the predicate to become [].

Here is an example demonstrating the use of the guard function in the Maybe monad.
Code available in example10.hs
data Record = Rec {name::String, age::Int} deriving Show
type DB = [Record]

-- getYoungerThan returns all records for people younger than a specified age.
-- It uses the guard function to eliminate records for ages at or over the limit.
-- This is just for demonstration purposes.  In real life, it would be
-- clearer to simply use filter.  When the filter criteria are more complex,
-- guard becomes more useful.
getYoungerThan :: Int -> DB -> [Record]
getYoungerThan limit db = mapMaybe (\r -> do { guard (age r < limit); return r }) db

Summary

Haskell provides a number of functions which are useful for working with monads in the standard libraries. The Monad class and most common monad functions are in the standard prelude. The MonadPlus class and less commonly-used (but still very useful!) functions are defined in the Monad module. Many other types in the Haskell libraries are declared as instances of Monad and MonadPlus in their respective modules.


Prev: Exercises TOC: Contents Next: Part II - Introduction