Skip to content

Add laws for MonadError and add tryError #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions Control/Monad/Error/Class.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Maintainer : [email protected]
Stability : experimental
Portability : non-portable (multi-parameter type classes)

[Computation type:] Computations which may fail or throw exceptions.
[Computation type:] Computations which may throw exceptions.

[Binding strategy:] Failure records information about the cause\/location
of the failure. Failure values bypass the bound function,
Expand All @@ -42,8 +42,10 @@ module Control.Monad.Error.Class (
Error(..),
MonadError(..),
liftEither,
tryError,
) where

import Control.Applicative (Applicative(pure))
import Control.Monad.Trans.Except (Except, ExceptT)
import Control.Monad.Trans.Error (Error(..), ErrorT)
import qualified Control.Monad.Trans.Except as ExceptT (throwE, catchE)
Expand All @@ -70,30 +72,39 @@ import Control.Monad.Instances ()
import Data.Monoid
import Prelude (Either(..), Maybe(..), either, (.), IO)

{- |
The strategy of combining computations that can throw exceptions
by bypassing bound functions
from the point an exception is thrown to the point that it is handled.

Is parameterized over the type of error information and
the monad type constructor.
It is common to use @'Either' String@ as the monad type constructor
for an error monad in which error descriptions take the form of strings.
In that case and many other common cases the resulting monad is already defined
as an instance of the 'MonadError' class.
You can also define your own error type and\/or use a monad type constructor
other than @'Either' 'String'@ or @'Either' 'IOError'@.
In these cases you will have to explicitly define instances of the 'MonadError'
class.
(If you are using the deprecated "Control.Monad.Error" or
"Control.Monad.Trans.Error", you may also have to define an 'Error' instance.)
-}
-- | Monads with a notion of exceptions which can be thrown and caught.
--
-- === Laws
--
-- 'catchError' and 'throwError' form a monad, with @('>>=')@
-- interpreted as 'catchError' and 'pure' as 'throwError'.
--
-- @
-- 'catchError' ('throwError' e) h = h e
-- 'catchError' m 'throwError' = m
-- 'catchError' ('catchError' m k) h = 'catchError' m (\\e -> 'catchError' (k e) h)
-- @
--
-- 'pure' and 'throwError' are left zeros for 'catchError' and @('>>=')@
-- respectively.
--
-- @
-- 'catchError' ('pure' a) h = 'pure' a
-- 'throwError' e '>>=' k = 'throwError' e
-- @
--
-- 'catchError' commutes with 'fmap' (it is a natual transformation).
--
-- @
-- 'fmap' f ('catchError' u h) = 'catchError' ('fmap' f u) ('fmap' f '.' h)
-- @
--
-- The last two laws are guaranteed by parametricity.
class (Monad m) => MonadError e m | m -> e where
-- | Is used within a monadic computation to begin exception processing.
-- | Throw an exception.
throwError :: e -> m a

{- |
A handler function to handle previous errors and return to normal execution.
{- | Handle an exception and return to normal execution.
A common idiom is:

> do { action1; action2; action3 } `catchError` handler
Expand All @@ -118,6 +129,11 @@ where @action1@ returns an 'Either' to represent errors.
liftEither :: MonadError e m => Either e a -> m a
liftEither = either throwError return

-- | Catch an exception and return it in 'Left', or return a successful result
-- in 'Right'.
tryError :: (MonadError e m, Applicative m) => m a -> m (Either e a)
tryError m = catchError (fmap Right m) (pure . Left)

instance MonadError IOException IO where
throwError = ioError
catchError = catch
Expand Down
2 changes: 2 additions & 0 deletions Control/Monad/Except.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ module Control.Monad.Except
-- * Monads with error handling
MonadError(..),
liftEither,
tryError,

-- * The ExceptT monad transformer
ExceptT(ExceptT),
Except,
Expand Down