%**The Haskell 98 Library Report: Monad Utilities
%**~header
\section{Monad Utilities}
\label{Monad}
\outline{
\inputHS{lib-hdrs/Monad}
}
The @Monad@ library defines the @MonadPlus@ class, and
provides some useful operations on monads.
\subsection{Naming conventions}
The functions in this library use the following naming conventions:
\begin{itemize}
\item
A postfix ``@M@'' always stands for a function in the Kleisli category:
@m@ is added to function results (modulo currying) and nowhere else.
So, for example,
\bprog
@
filter :: (a -> Bool) -> [a] -> [a]
filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
@
\eprog
\item A postfix ``@_@'' changes the result type from @(m a)@ to @(m ())@.
Thus (in the @Prelude@):
\bprog
@
sequence :: Monad m => [m a] -> m [a]
sequence_ :: Monad m => [m a] -> m ()
@
\eprog
\item A prefix ``@m@'' generalises an existing function to a monadic form.
Thus, for example:
\bprog
@
sum :: Num a => [a] -> a
msum :: MonadPlus m => [m a] -> m a
@
\eprog
\end{itemize}
\subsection{Class @MonadPlus@}
The @MonadPlus@ class is defined as follows:
\bprog
@
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
@
\eprog
The class methods @mzero@ and @mplus@ are the zero and plus
of the monad.
Lists and the @Maybe@ type are instances of @MonadPlus@, thus:
\bprog
@
instance MonadPlus Maybe where
mzero = Nothing
Nothing `mplus` ys = ys
xs `mplus` ys = xs
instance MonadPlus [] where
mzero = []
mplus = (++)
@
\eprog
\subsection{Functions}
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.
% There is no convincing small-scale example for mapAndUnzipM
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,
\par
{\small
\bprog
@
listFile :: String -> IO ()
listFile nm =
do cts <- readFile nm
zipWithM_ (\i line -> do putStr (show i); putStr ": "; putStrLn line)
[1..]
(lines cts)
@
\eprog
}
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.
\bprog
@
foldM f a1 [x1, x2, ..., xm ]
==
do
a2 <- f a1 x1
a3 <- f a2 x2
...
f am xm
@
\eprog
If right-to-left
evaluation is required, the input list should be reversed.
% Omitted for now. These functions are very useful in parsing libraries
% - but in a slightly modified form:
% o It is conventional to return the _longest_ parse first - not
% shortest first.
% o The function is too strict - you can't get any part of the result
% until the entire parse completes. The fix is to use the function
% force when defining zeroOrMore.
%
% force :: Parser a -> Parser a
% force (P m) = P (\i -> let x = p i in
% (fst (head x), snd (head x)) : tail x)
%
% but how are we to generalise this to an arbitrary monad?
%
% The @zeroOrMore@ function performs an action repeatedly - returning
% the list of all results obtained. The @oneOrMore@ function is similar
% but the action must succeed at least once. That is,
% \bprog
%
% zeroOrMore m = zero ++
% [ [a0] | a0 <- m ] ++
% [ [a0,a1] | a0 <- m, a1 <- m ] ++
% [ [a0,a1,a2] | a0 <- m, a1 <- m, a2 <- m ] ++
% ...
%
% oneOrMore m = [ [a0] | a0 <- m ] ++
% [ [a0,a1] | a0 <- m, a1 <- m ] ++
% [ [a0,a1,a2] | a0 <- m, a1 <- m, a2 <- m ] ++
% ...
%
% \eprog
The @when@ and @unless@ functions provide conditional execution of
monadic expressions. For example,
\bprog
@
when debug (putStr "Debugging\n")
@
\eprog
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,
\bprog
@
liftM2 (+) [0,1] [0,2] = [0,2,1,3]
liftM2 (+) (Just 1) Nothing = Nothing
@
\eprog
In many situations, the @liftM@ operations can be replaced by uses
of @ap@, which promotes function application.
\bprog
@
return f `ap` x1 `ap` ... `ap` xn
@
\eprog
is equivalent to
\bprog
@
liftMn f x1 x2 ... xn
@
\eprog
\clearpage
\subsection{Library {\tt Monad}}
\inputHS{lib-code/Monad}
%**~footer