In this particular case, you do not need to stack two transformers--since both are MonadError String
, they can be used as the same monad. You can use errorm
and errorms
together just like you would use two values in any other monad.
As a more concrete explanation, ignore the transformers for a second: you can imagine that the values are just Either String Int
and Either String String
. Clearly, you can just use them together. This is why you only need one runErrorT
at the end rather than two: both values are in the same monad.
Now, what about your actual question: how do you stack two monad transformers? It works just like combining any two monad transformers. Two state transformers stacked upon each other look just like two different transformers stacked upon each other.
Now, it is a little tricky to use them. Depending on which one you're using, you will need to use lift
differently. If you have a value in the base monad, you need to lift twice. If you have a value in the inner state monad, you will need to use it once. And if you have one in the outer level, you won't need it at all. This is just like normal transformers.
Going back to your error example, let's imagine you actually did want to stack two different error monad transformers instead of using them as one. This would mean that if you want to throw an error in the inner one, you would have to write lift (throwError "message")
. If you had actually done this and had two stacked error transformers, than using runErrorT
twice would have worked.