1
votes

Given a mtl monad stack, e.g. ExceptT String (WriterT String (State s a)), how can I evaluate the inner state monad without needing to unwrap the outer monads?

have :: ExceptT String (WriterT String (State s)) a
f    :: State s a -> a

want :: ExceptT String (WriterT String Identity) a

I can do this by calling runExceptT followed by runWriterT and repacking the results afterward but it seems like the wrong way to accomplish this.


As far as I tried, something like fmap or similar won't work because the monad transformer stack is treated as an entire monad on it's own. What I need is a functionality to "split" the monad transformer stack like this:

split :: (MonadTrans s, Monad t) => (s t) a -> s (t a)

Either I haven't found this function or the solution works entirely differently.

2
You can't. All a transformer adds is lift :: m a -> t m a. There's no generic way to implement what you're trying to do given just Monad and MonadTrans (the whole notion of "running" things isn't even part of the monad transformer concept). You can have specialized functions like in the answer, but no generic solution.Cubic

2 Answers

5
votes

I'm not aware of anything as general as your split, but using the map...T functions could prevent the explicit manipulation:

have :: ExceptT String (WriterT String (State s)) a
f    :: State s a -> a

want :: ExceptT String (WriterT String Identity) a
want = mapExceptT (mapWriterT (return . f)) have
3
votes

The approach that seems simplest in this particular case is to use the MFunctor instances of the ExceptT e and WriterT w transformers:

import Control.Monad.Morph

floop :: Monad m
      => s
      -> ExceptT e (WriterT w (StateT s m)) a
      -> ExceptT e (WriterT w m) a
floop s = hoist (hoist $ flip evalStateT s)

Since State s = StateT s Identity, the slight generalization above is immediate.