%**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