module ComputingWithMonads.MonadError whereimport Prelude
import Effect (Effect)
import Effect.Console (log)
import Data.Identity (Identity(..))
import Data.Either (Either(..))
import Data.Maybe (Maybe(..))
import Control.Monad.Error.Class (catchError, catchJust, try, withResource)
import Control.Monad.Except (Except, runExcept)
import Control.Monad.Except.Trans (ExceptT(..))
main :: EffectUnitmain = do
runMainFunction
log "=== Derived Functions ==="
example_catchJust
example_try
example_withResource
computationThatFailsWith :: forall e. e -> Except e IntcomputationThatFailsWith error = ExceptT (
-- A computationIdentity (
-- that failed and produced an errorLeft error
)
)
computationThatSucceedsWith :: forall e a. a -> Except e a
computationThatSucceedsWith a = ExceptT (
-- A computationIdentity (
-- that succeeded and produced the outputRight a
)
)
compute :: forall e a. Show e => Show a => Except e a -> EffectUnitcompute theComputation =
case runExcept theComputation ofLeft error -> log $ "Failed computation! Error was: " <> show error
Right output -> log $ "Successful computation! Output: " <> show output
runMainFunction :: EffectUnitrunMainFunction = do
log "catchError:"
compute (
catchError
(computationThatFailsWith "An error string")
-- and a function that successfully handles the error
(\errorString -> ExceptT (pure $ Right5))
)
compute (
catchError
(computationThatFailsWith "An error string")
-- and a function that cannot handle the error successfully
(\errorString -> ExceptT (pure $ Left errorString))
)
-------------------dataErrorType
= FailedCompletely
| CanHandleTheseErrorsdataTheseErrors
= Error1
| Error2example_catchJust :: EffectUnitexample_catchJust = do
log "catchJust:"-- fail with an error that we ARE NOT catching...
compute
(catchJust
ignore_FailedCompletely
(computationThatFailsWith FailedCompletely)
-- this function is never run because-- we ignore the "FailedCompletely" error instance
handleError
)
-- fail with an error that we ARE catching...
compute
(catchJust
ignore_FailedCompletely
(computationThatFailsWith (CanHandleError1))
-- this function is run because we accept the-- error instance. It would also work if we threw `Error2`
handleError
)
ignore_FailedCompletely :: ErrorType -> MaybeTheseErrorsignore_FailedCompletelyFailedCompletely = Nothingignore_FailedCompletely (CanHandle error) = Just error
handleError :: TheseErrors -> ExceptErrorTypeInthandleErrorError1 = ExceptT (pure $ Right5)
handleErrorError2 = ExceptT (pure $ Right6)
instanceShowErrorTypewhere
show FailedCompletely = "FailedCompletely"
show (CanHandle error) = "CanHandle2 (" <> show error <> ")"instanceShowTheseErrorswhere
show Error1 = "Error1"
show Error2 = "Error2"-------------------example_try :: EffectUnitexample_try = do
log "try: "
compute' (try $ computationThatSucceedsWith 5)
compute' (try $ computationThatFailsWith "an error occurred!")
-- In `try`, both the error and output isntance is returned,-- thereby exposing it for usage in the do notation. To account for this,-- we've modified `compute` slightly below.-- Also, since we only specify either the error type or the output type above,-- type inference can't figure out what the other type is. So,-- it thinks that the unknown type doesn't have a "Show" instance-- and the compilation fails.-- Thus, we also specify both types below to avoid this problem.compute' :: ExceptString (EitherStringInt) -> EffectUnitcompute' theComputation =
case runExcept theComputation ofLeft error -> log $ "Failed computation! Error was: " <> show error
Right e_or_a -> case e_or_a ofLeft e -> log $ "Exposed error instance in do notation: " <> show e
Right a -> log $ "Exposed output instance in do notation: " <> show a
-------------------dataResource = ResourceinstanceShowResourcewhere
show x = "Resource"example_withResource :: EffectUnitexample_withResource = do
log "withResource: "
compute (
withResource
getResource
cleanupResource
computationThatUseResource
)
getResource :: ExceptStringResourcegetResource = computationThatSucceedsWith ResourcecleanupResource :: Resource -> ExceptStringUnitcleanupResource r =
-- resource is cleaned up here-- and when finished, we return unitExceptT (pure $ Right unit)
computationThatUseResource :: Resource -> ExceptStringIntcomputationThatUseResource r = -- do-- use resource here to compute some valueExceptT (pure $ Right5)