{- | Module : Control.Monad.Except Copyright : (c) Michael Weber <michael.weber@post.rwth-aachen.de> 2001, (c) Jeff Newbern 2003-2006, (c) Andriy Palamarchuk 2006 License : BSD-style (see the file LICENSE) Maintainer : ross@soi.city.ac.uk Stability : experimental Portability : non-portable (type families) [Computation type:] Computations which may fail or throw exceptions. [Binding strategy:] Failure records information about the cause\/location of the failure. Failure values bypass the bound function, other values are used as inputs to the bound function. [Useful for:] Building computations from sequences of functions that may fail or using exception handling to structure error handling. [Zero and plus:] Zero is represented by an empty error and the plus operation executes its second argument if the first fails. [Example type:] @'Data.Either' String a@ The Error monad (also called the Exception monad). -} {- Rendered by Michael Weber <mailto:michael.weber@post.rwth-aachen.de>, inspired by the Haskell Monad Template Library from Andy Gill (<http://web.cecs.pdx.edu/~andy/>) -} module Control.Monad.Except ( -- * Monads with error handling MonadError(..), tryError, withError, -- * The ErrorT monad transformer ExceptT(..), runExceptT, mapExceptT, withExceptT, module Control.Monad, module Control.Monad.Fix, module Control.Monad.Trans, -- * Example 1: Custom Error Data Type -- $customErrorExample -- * Example 2: Using ExceptT Monad Transformer -- $ExceptTExample ) where import Control.Monad.Except.Class import Control.Monad.Trans import Control.Monad.Trans.Except (ExceptT(..), runExceptT, mapExceptT, withExceptT) import Control.Monad import Control.Monad.Fix -- | 'MonadError' analogue to the 'Control.Exception.try' function. tryError :: (MonadError m) => m a -> m (Either (ErrorType m) a) tryError :: forall (m :: * -> *) a. MonadError m => m a -> m (Either (ErrorType m) a) tryError m a action = (forall a b. b -> Either a b Right forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b <$> m a action) forall (m :: * -> *) a. MonadError m => m a -> (ErrorType m -> m a) -> m a `catchError` (forall (f :: * -> *) a. Applicative f => a -> f a pure forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a b. a -> Either a b Left) -- | 'MonadError' analogue to the 'withExceptT' function. -- Modify the value (but not the type) of an error. -- The type is fixed because of the 'ErrorType' type family. withError :: (MonadError m) => (ErrorType m -> ErrorType m) -> m a -> m a withError :: forall (m :: * -> *) a. MonadError m => (ErrorType m -> ErrorType m) -> m a -> m a withError ErrorType m -> ErrorType m f m a action = forall (m :: * -> *) a. MonadError m => m a -> m (Either (ErrorType m) a) tryError m a action forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= forall a c b. (a -> c) -> (b -> c) -> Either a b -> c either (forall (m :: * -> *) a. MonadError m => ErrorType m -> m a throwError forall b c a. (b -> c) -> (a -> b) -> a -> c . ErrorType m -> ErrorType m f) forall (f :: * -> *) a. Applicative f => a -> f a pure {- $customErrorExample Here is an example that demonstrates the use of a custom 'Error' data type with the 'throwError' and 'catchError' exception mechanism from 'MonadError'. The example throws an exception if the user enters an empty string or a string longer than 5 characters. Otherwise it prints length of the string. >import Control.Monad.Except > >-- This is the type to represent length calculation error. >data LengthError = EmptyString -- Entered string was empty. > | StringTooLong Int -- A string is longer than 5 characters. > -- Records a length of the string. > | OtherError String -- Other error, stores the problem description. > >-- Converts LengthError to a readable message. >instance Show LengthError where > show EmptyString = "The string was empty!" > show (StringTooLong len) = > "The length of the string (" ++ (show len) ++ ") is bigger than 5!" > show (OtherError msg) = msg > >-- For our monad type constructor, we use Either LengthError >-- which represents failure using Left LengthError >-- or a successful result of type a using Right a. >type LengthMonad = Either LengthError > >main = do > putStrLn "Please enter a string:" > s <- getLine > reportResult (calculateLengthOrFail s) > >-- Attempts to calculate length and throws an error if the provided string is >-- empty or longer than 5 characters. >-- The processing is done in Either monad. >calculateLengthOrFail :: String -> LengthMonad Int >calculateLengthOrFail [] = throwError EmptyString >calculateLengthOrFail s | len > 5 = throwError (StringTooLong len) > | otherwise = return len > where len = length s > >-- Prints result of the string length calculation. >reportResult :: LengthMonad Int -> IO () >reportResult (Right len) = putStrLn ("The length of the string is " ++ (show len)) >reportResult (Left e) = putStrLn ("Length calculation failed with error: " ++ (show e)) -} {- $ExceptTExample @'ErrorT'@ monad transformer can be used to add error handling to another monad. Here is an example how to combine it with an @IO@ monad: >import Control.Monad.Except > >-- An IO monad which can return String failure. >-- It is convenient to define the monad type of the combined monad, >-- especially if we combine more monad transformers. >type LengthMonad = ExceptT String IO > >main = do > -- runExceptT removes the ExceptT wrapper > r <- runExceptT calculateLength > reportResult r > >-- Asks user for a non-empty string and returns its length. >-- Throws an error if user enters an empty string. >calculateLength :: LengthMonad Int >calculateLength = do > -- all the IO operations have to be lifted to the IO monad in the monad stack > liftIO $ putStrLn "Please enter a non-empty string: " > s <- liftIO getLine > if null s > then throwError "The string was empty!" > else return $ length s > >-- Prints result of the string length calculation. >reportResult :: Either String Int -> IO () >reportResult (Right len) = putStrLn ("The length of the string is " ++ (show len)) >reportResult (Left e) = putStrLn ("Length calculation failed with error: " ++ (show e)) -}