Overview
MonadThrow
MonadThrow
is used to immediately stop bind
's sequential computation and return a value of its error type because of some unforeseeable error (e.g. error encountered when connecting to a database, file that was supposed to exist did not exist, etc).
It's default implmentation is ExceptT
:
-- e m a
newtype ExceptT error monad output =
ExceptT (monad (Either error output))
-- Pseudo-syntax: combines class and instancee together:
class (Monad m) => MonadThrow e (ExceptT e m) where
throwError :: forall a. e -> ExceptT e m a
throwError a = ExceptT (pure $ Left a)
ExceptT: Before and After
Before using ExceptT
, we would write this ugly verbose code:
getBestCompany :: Industry -> Effect (Either Error Company)
getBoss :: Company -> Effect (Either Error Name)
main :: Effect Unit
main = do
eitherCompany <- getBestCompany ComputerIndustry
case eitherCompany of
Left error -> log $ "Error: " <> show error
Right bestCompany -> do
eitherName <- getBoss bestCompany
case eitherName of
Left error -> log $ "Error: " <> show error
Right name -> do
log $ "The name of the best company is: " <> name
After using ExceptT
, we would write this clear readable code:
getBestCompany :: Industry -> Effect (Either Error Company)
getBoss :: Company -> Effect (Either Error Name)
main :: Effect Unit
main = do
eitherResult <- runExceptT do
bestCompany <- getBestCompany ComputerIndustry
getBoss bestCompany
case eitherResult of
Left error -> log $ "Error: " <> show error
Right name -> do
log $ "The name of the best company is: " <> name
MonadError
MonadError
extends MonadThrow
by enabling a monad to catch the thrown error, attempt to handle it (by changing the error type to an output type), and then continue bind
's sequential computation. If catchError
can't handle the error, bind
's sequential computation will still stop at that point and return the value of the error type.
newtype ExceptT e m a = ExceptT (m (Either e a))
class (Monad m) => MonadError e (ExceptT e m) where
catchError :: forall a. ExceptT e m a -> (e -> ExceptT e m a) -> ExceptT e m a
catchError (ExceptT m) handleError =
ExceptT (m >>= (\either_E_or_A -> case either_E_or_A of
Left e -> case handleError e of ExceptT b -> b
Right a -> pure $ Right a))
For example,
getFileContents :: forall m.
MonadError m =>
String ->
m String
getFileContents pathToFile = do
readFileContents pathToFile `catchError` \fileNotFound ->
pure defaultValue
where
defaultValue = "foo"
Derived Functions
MonadThrow
does not have any derived functions.
MonadError
has 3 functions:
catchJust
: catch only the errors you want to try to handle and ignore the otherstry
: expose the error value (if computation fails) for usage in the do notationwithResource
: whether a computation fails or succeeds, clean up resources after it is done
Do Notation
Since MonadThrow/MonadError are error-related, we'll show the do notation in meta-language here since it will be harder to do so in the code examples:
-- MonadThrow
stopped <- throwError e
value1 <- otherComputation stopped
value2 <- otherComputation value1
-- MonadError
mightRun <- computationThatMayFail `catchError` computationWhenPreviousFailed
left_Error <- try computationThatFails
right_Output <- try computationThatSucceeds
output <- withResource getResource cleanup computationThatUsesResource
Laws, Instances, and Miscellaneous Functions
For its laws, see
For ExceptT
's instances, see
To handle/modify the output of an error computation: