From 5ae725533610e93c49b407dbee339bc6e6b5f8f4 Mon Sep 17 00:00:00 2001 From: Lysxia Date: Mon, 4 Mar 2019 11:43:13 -0500 Subject: [PATCH 1/2] Add tryError --- Control/Monad/Error/Class.hs | 7 +++++++ Control/Monad/Except.hs | 2 ++ 2 files changed, 9 insertions(+) diff --git a/Control/Monad/Error/Class.hs b/Control/Monad/Error/Class.hs index cc61776..56d3d72 100644 --- a/Control/Monad/Error/Class.hs +++ b/Control/Monad/Error/Class.hs @@ -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) @@ -118,6 +120,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 diff --git a/Control/Monad/Except.hs b/Control/Monad/Except.hs index 4803c49..ef5f17c 100644 --- a/Control/Monad/Except.hs +++ b/Control/Monad/Except.hs @@ -38,6 +38,8 @@ module Control.Monad.Except -- * Monads with error handling MonadError(..), liftEither, + tryError, + -- * The ExceptT monad transformer ExceptT(ExceptT), Except, From 273530ad1808332f4b5ce3b6bb3f16d2aba25230 Mon Sep 17 00:00:00 2001 From: Lysxia Date: Mon, 4 Mar 2019 11:43:22 -0500 Subject: [PATCH 2/2] Add laws for MonadError --- Control/Monad/Error/Class.hs | 53 +++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/Control/Monad/Error/Class.hs b/Control/Monad/Error/Class.hs index 56d3d72..58a710a 100644 --- a/Control/Monad/Error/Class.hs +++ b/Control/Monad/Error/Class.hs @@ -16,7 +16,7 @@ Maintainer : libraries@haskell.org 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, @@ -72,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