{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TypeApplications #-} import Test.Tasty import Test.Tasty.QuickCheck import Test.Monad.Except.Checkers (checkExcept) import Test.SmallList (SmallList) import Test.QuickCheck.HigherOrder import Control.Monad.State import Control.Monad.Except newtype PrismErrorT f e m a = PrismErrorT { runPrismErrorT :: m a } deriving (Functor, Applicative, Monad, Eq, Ord, Show, TestEq, Constructible) class Prism e f where match :: e -> Either e f build :: f -> e instance Prism (Either a b) a where match (Left a) = Right a match e = Left e build = Left instance (MonadError e m, Prism e f) => MonadError f (PrismErrorT f e m) where throwError f = PrismErrorT (throwError (build f)) catchError (PrismErrorT m) handleF = PrismErrorT (catchError m (\e -> case match e of Left e -> throwError e Right f -> runPrismErrorT (handleF f))) trans :: forall m e f a. (MonadError e m, Prism e f) => (forall n. MonadError f n => n a) -> m a trans g = runPrismErrorT g type M = PrismErrorT Int (Either Int Word) (ExceptT (Either Int Word) (StateT Int SmallList)) main :: IO () main = defaultMain . testProperties "PrismError" $ checkExcept @M @Int @Int