I stumbled upon a recording of a talk by George Wilson on the monad transformer library (MTL) in Haskell. He goes on to demonstrate how monad transformers can be made more composable by using constraints instead of directly putting a monad transformer in the return type of a function. What is presented there is definitely an improvement, however it is still unsatisfactory. In particular, in his examples there is no separation of describing and running a program, which means everything happens in the IO monad (the MonadIO constraint in function signatures in the examples). In other words, anything goes and such functions are again too powerful.
Can one avoid specifying right away what effect is at a particular stack level in a monad transformer? Can a monad transformer be used to stack embedded domain-specific languages (eDSLs) instead of effects? In such a case, a monad transformer would give a stack of eDSLs that would get interpreted by an interpreter. I am not sure if this is even achievable and what it would represent, but it seems to me it would be something similar to free monads.