effin-0.1.0.0: src/Control/Effect/Exception.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
#if MTL
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
#endif
module Control.Effect.Exception (
EffectException, Exception, runException,
raise, except, finally
) where
import Control.Monad.Effect
#ifdef MTL
import qualified Control.Monad.Error.Class as E
instance EffectException e es => E.MonadError e (Effect es) where
throwError = raise
catchError = except
#endif
-- | An effect that describes the possibility of failure.
newtype Exception e a = Exception { unException :: e }
deriving Functor
type EffectException e es = (Member (Exception e) es, e ~ ExceptionType es)
type family ExceptionType es where
ExceptionType (Exception e ': es) = e
ExceptionType (e ': es) = ExceptionType es
-- | Raises an exception.
raise :: EffectException e es => e -> Effect es a
raise = send . Exception
-- | Handles an exception. Intended to be used in infix form.
--
-- > myComputation `except` \ex -> doSomethingWith ex
except :: EffectException e es => Effect es a -> (e -> Effect es a) -> Effect es a
except = flip run
where
run handler =
handle return
$ intercept (handler . unException)
$ defaultRelay
-- | Ensures that a computation is run after another one completes,
-- regardless of whether an exception was raised. Intended to be
-- used in infix form.
--
-- > do x <- loadSomeResource
-- > doSomethingWith x `finally` unload x
finally :: EffectException e es => Effect es a -> Effect es () -> Effect es a
finally effect finalizer = do
result <- effect `except` \e -> do
finalizer
raise e
finalizer
return result
-- | Completely handles an exception effect.
runException :: Effect (Exception e ': es) a -> Effect es (Either e a)
runException =
handle (return . Right)
$ eliminate (return . Left . unException)
$ defaultRelay