{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE TypeApplications #-} import Control.Monad.Classes hiding (exec) import Control.Monad.Classes.Log import Control.Monad.Log (LoggingT, runLoggingT) import Control.Monad.Trans.Reader (ReaderT(..)) import Control.Monad.Trans.State (State, execState) import Test.Tasty import Test.Tasty.HUnit main :: IO () main = defaultMain $ testCase "should compile" testcase where testcase = assertEqual "correct results" [RRes "bar", LRes "foo"] (exec op) data Foo = Foo String data Bar = Bar String data Res = LRes String | RRes String deriving (Eq, Ord, Show) logit :: (MonadReader a m, MonadLog a m) => (a -> a) -> m () logit f = ask >>= logMessage . f -- | This is the operation that will not compile under mtl -- | due to functional dependencies. op :: (MonadReader Foo m, MonadReader Bar m, MonadLog Foo m, MonadLog Bar m) => m () op = logit (id @Foo) >> logit (id @Bar) -- | handler for Foo foo :: MonadState [Res] m => Foo -> m () foo (Foo s) = modify $ \r -> LRes s : r -- | Handler for Bar bar :: MonadState [Res] m => Bar -> m () bar (Bar s) = modify $ \r -> RRes s : r exec :: ReaderT Foo (ReaderT Bar (LoggingT Foo (LoggingT Bar (State [Res])))) () -> [Res] exec m = execState (runLoggingT (runLoggingT (runReaderT (runReaderT m (Foo "foo")) (Bar "bar")) foo) bar) []