There may be another third way to encode effects:
Maybe it is possible to demonstrate that every effect may be a combination of state (data) and continuations (processing). I don´t know if this is true, but it is very likely.
If a monad can handle user defined states (in a pure way, like the state monad) and continuations, then the programmer can implement any new effect by combining them.
With continuation effect I mean that each monadic statement can inspect and make use of his own computation in which it is inserted (his closure) and his continuation.
The effects are added by creating new primitives, instead of aggregating new monad transformers (mtl) or declaring new effects (the free monad).
I implemented reactivity, backtracking, streaming and other high level effects besides readers, writers and other conventional effects using this approach, in the package transient.
The advantage is Expressive power (high level effects), composability, simple type signatures, and extensibility by means of a single expression.
It may be necessary to have more than one monad when we want to enforce certain effects that are performed when one monad is converted into to another, trough the type system