These functions were formerly located in a module called "HugsUtils" -- but it was too messy to make it a "standard Hugs library" so we moved it over here. > module Haskore.General.Monad where > import Control.Monad (MonadPlus, mplus, liftM2) ToDo: decide on appropriate fixities for these functions \begin{haskelllisting} > infixr 2 `andOnError`, `butOnError` > > assert :: Bool -> String -> IO () > assert True _ = return () > assert False msg = ioError (userError msg) \end{haskelllisting} Resource (de)allocation can interact badly with error handling code. For example, even if the programmer has taken care that every resource allocation is paired with an appropriate deallocation, they might forget to release resources when an exception is invoked. For example, this program would fail to close \code{outFile} if an error occured while operating on one of the \code{inFile}s. \begin{haskelllisting} cat :: String -> [String] -> IO () cat outfile files = do outFile <- open outfile WriteMode mapM_ (\file -> do inFile <- open file ReadMode copy inFile outFile close inFile ) files close outFile \end{haskelllisting} The following functions provide ways of ensuring that a piece of "cleanup code" is executed even if an exception is raised. \begin{itemize} \item \lstinline!m `andOnError` k! is like \lstinline!m >> k! except that \code{k} gets executed even if an exception is raised in \code{m}. \item \lstinline!m `butOnError` k! is like \code{m} except that \code{k} gets executed if an exception is raised in \code{m}. \end{itemize} For example, the following version of \code{cat} guarantees to close all files even if an error occurs. \begin{haskelllisting} cleancat :: String -> [String] -> IO () cleancat outfile files = do outFile <- open outfile WriteMode mapM_ (\file -> do open file ReadMode >>= \ inFile -> copy inFile outFile `andOnError` close inFile ) files `andOnError` close outFile \end{haskelllisting} \begin{haskelllisting} > andOnError :: IO a -> IO b -> IO b > m `andOnError` k = (m `catch` \e -> k >> ioError e) >> k \end{haskelllisting} Use this to add some cleanup code k that only gets executed if an error occurs during execution of m. \begin{haskelllisting} > butOnError :: IO a -> IO () -> IO a > m `butOnError` k = (m `catch` \e -> k >> ioError e) > zeroOrMore, oneOrMore :: MonadPlus m => m a -> m [a] > zeroOrMore m = return [] `mplus` oneOrMore m > oneOrMore m = liftM2 (:) m (zeroOrMore m) \end{haskelllisting} Repeat the action \code{m} until the result fulfills \code{p}. \begin{haskelllisting} > untilM :: Monad m => (a -> Bool) -> m a -> m a > untilM p m = > do x <- m > if p x then return x else untilM p m \end{haskelllisting}