{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE UndecidableInstances #-} module Polysemy.ConstraintAbsorber.MonadError ( absorbError ) where import qualified Control.Monad.Error.Class as S import Polysemy import Polysemy.ConstraintAbsorber import Polysemy.Error ------------------------------------------------------------------------------ -- | Introduce a local 'S.MonadError' constraint on 'Sem' --- allowing it to -- interop nicely with MTL. -- -- @since 0.3.0.0 absorbError :: Member (Error e) r => (S.MonadError e (Sem r) => Sem r a) -- ^ A computation that requires an instance of 'S.MonadError' for -- 'Sem'. This might be something with type @'S.MonadError' e m => m a@. -> Sem r a absorbError = absorbWithSem @(S.MonadError _) @Action (ErrorDict throw catch) (Sub Dict) {-# INLINEABLE absorbError #-} ------------------------------------------------------------------------------ -- | A dictionary of the functions we need to supply -- to make an instance of Error data ErrorDict e m = ErrorDict { throwError_ :: forall a. e -> m a , catchError_ :: forall a. m a -> (e -> m a) -> m a } ------------------------------------------------------------------------------ -- | Wrapper for a monadic action with phantom -- type parameter for reflection. -- Locally defined so that the instance we are going -- to build with reflection must be coherent, that is -- there cannot be orphans. newtype Action m s' a = Action { action :: m a } deriving (Functor, Applicative, Monad) ------------------------------------------------------------------------------ -- | Given a reifiable mtl Error dictionary, -- we can make an instance of @MonadError@ for the action -- wrapped in @Action@. instance ( Monad m , Reifies s' (ErrorDict e m) ) => S.MonadError e (Action m s') where throwError e = Action $ throwError_ (reflect $ Proxy @s') e {-# INLINEABLE throwError #-} catchError x f = Action $ catchError_ (reflect $ Proxy @s') (action x) (action . f) {-# INLINEABLE catchError #-}