The Haskell 1.4 Library Report
top | back | next | contents

10  Monad Utilities


module Monad (
    join, mapAndUnzipM, zipWithM, foldM, when, unless, ap,
    liftM, liftM2, liftM3, liftM4, liftM5
    ) where

join             :: (Monad m) => m (m a) -> m a
mapAndUnzipM     :: (Monad m) => (a -> m (b,c)) -> [a] -> m ([b], [c])
zipWithM         :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m [c]
foldM            :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
zeroOrMore       :: (MonadPlus m) => m a -> m [a]
oneOrMore        :: (MonadPlus m) => m a -> m [a]
when             :: (Monad m) => Bool -> m () -> m ()
unless           :: (Monad m) => Bool -> m () -> m ()
ap  :: (Monad a) => (m (a -> b)) -> (m a) -> m b
liftM            :: (Monad m) => (a -> b) -> (m a -> m b)
liftM2           :: (Monad m) => (a -> b -> c) -> (m a -> m b -> m c)
liftM3           :: (Monad m) => (a -> b -> c -> d) ->
                                 (m a -> m b -> m c -> m d)
liftM4           :: (Monad m) => (a -> b -> c -> d -> e) ->
                                 (m a -> m b -> m c -> m d -> m e)
liftM5           :: (Monad m) => (a -> b -> c -> d -> e -> f) ->
                                 (m a -> m b -> m c -> m d -> m e -> m f)

These utilities provide some useful operations on monads.

The join function is the conventional monad join operator. It is used to remove one level of monadic structure, projecting its bound argument into the outer level.

The mapAndUnzipM function maps its first argument over a list, returning the result as a pair of lists. This function is mainly used with complicated data structures or a state-transforming monad.

The zipWithM function generalises zipWith to arbitrary monads. For instance the following function displays a file, prefixing each line with its line number,

listFile :: String -> IO ()
listFile nm =
  do cts <- openFile nm
     zipWithM_ (\i line -> do putStr (show i); putStr ": "; putStrLn line)
               [1..]
               (lines cts)

The foldM function is analogous to foldl, except that its result is encapsulated in a monad. Note that foldM works from left-to-right over the list arguments. This could be an issue where (>>) and the "folded function" are not commutative.

    foldM f a1 [x1, x2, ..., xm ]
==  
    do
      a2 <- f a1 x1
      a3 <- f a2 x2
      ...
      f am xm

If right-to-left evaluation is required, the input list should be reversed.

The when and unless functions provide conditional execution of monadic expressions. For example,

when debug (putStr "Debugging\n")

will output the string "Debugging\n" if the Boolean value debug is True, and otherwise do nothing.

The monadic lifting operators promote a function to a monad. The function arguments are scanned left to right. For example,

liftM2 (+) [0,1] [0,2] = [0,2,1,3]
liftM2 (+) (Just 1) Nothing = Nothing

In many situations, the liftM operations can be replaced by uses of ap, which promotes function application.

return f `ap` x1 `ap` ... `ap` xn

is equivalent to

liftMn f x1 x2 ... xn

10.1  Library Monad


module Monad (
    join, mapAndUnzipM, zipWithM, zipWithM_, foldM, when, unless, ap,
    liftM, liftM2, liftM3, liftM4, liftM5
    ) where

join             :: (Monad m) => m (m a) -> m a
join x           =  x >>= id

mapAndUnzipM     :: (Monad m) => (a -> m (b,c)) -> [a] -> m ([b], [c])
mapAndUnzipM f xs = accumulate (map f xs) >>= return . unzip

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

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

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

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               :: (Monad m) => m (a -> b) -> m a -> m b
ap               =  liftM2 ($)

liftM            :: (Monad m) => (a -> b) -> (m a -> m b)
liftM f          =  \a -> [f a' | a' <- a]

liftM2           :: (Monad m) => (a -> b -> c) -> (m a -> m b -> m c)
liftM2 f         =  \a b -> [f a' b' | a' <- a, b' <- b]  

liftM3           :: (Monad m) => (a -> b -> c -> d) ->
                                 (m a -> m b -> m c -> m d)
liftM3 f         =  \a b c -> [f a' b' c' | a' <- a, b' <- b, c' <- c]  

liftM4           :: (Monad m) => (a -> b -> c -> d -> e) ->
                                 (m a -> m b -> m c -> m d -> m e)
liftM4 f         =  \a b c d -> [f a' b' c' d' |
                                 a' <- a, b' <- b, c' <- c, d' <- d]  

liftM5           :: (Monad m) => (a -> b -> c -> d -> e -> f) ->
                                 (m a -> m b -> m c -> m d -> m e -> m f)
liftM5 f         =  \a b c d e -> [f a' b' c' d' e' |
                                   a' <- a, b' <- b,
                                   c' <- c, d' <- d, e <- e']


The Haskell 1.4 Library Report
top | back | next | contents
April 4, 1997