
As I say, every time I've tried to do this, I end up writing a function to "run this stuff", and it typically takes a few hours to reach the point where it type-checks.
It took me a while the first time, but then I just learned the pattern and I do it that way every time. Here's my pattern: type SomethingStack m = Monad1T Args (Monad2T Args (Monad3T Args m)) newtype SomethingT m a = SomethingT (SomethingStack m a) deriving (Functor, Monad, MonadIO, MonadError MyError, KitchenSink) run_something_t (SomethingT m) = m run :: (Monad m) => SomethingT m a -> m (a, MonadCrap, MonadCrap, ...) run = Monad3T.run args . Monad2T.run args . Monad1T.run args . run_something_t Or if you don't need the polymorphism, just stick a 'Identity.runIdentity' before Monad3T.run and make a type Something = SomethingT Identity The tricky bit is that you run them inside-out so the composition looks like the stack backwards. And sometimes mtl's 'run' functions have an inconvenient arg order (e.g. StateT), so you have to flip them.