[Haskell-cafe] Improving MTL instances (was: Overlapping/Incoherent instances)

Ryan Ingram ryani.spam at gmail.com
Mon Oct 13 03:29:44 EDT 2008


On Mon, Oct 13, 2008 at 2:04 AM, J. Garrett Morris
<jgmorris at cecs.pdx.edu> wrote:
> Indeed - MTL seems to have been rewritten at some point in the past to
> prefer exhaustive enumeration to overlap.

Indeed, and I actually think this is a weakness of the current
implementation.  Anyone who comes up with a new transformer that
provides different functionality than what is there needs to
explicitly provide all the relevant instances, instead of letting
MonadTrans do its thing.

Consider MonadPrompt (shameless plug, it's on hackage!)  In order to
be fully interoperable with the MTL I'd need to write instances for
MonadState, MonadReader, MonadWriter, MonadError, and MonadCont for
PromptT.  These are unavoidable, although for monads with a "simple
enough" interface, such as State, everything can be accomplished with
"lift".

But I also need to provide the same boilerplate instances for every
other monad transformer in the package to give them instances of
MonadPrompt.  And MonadPrompt *does* have a "simple enough" interface
that it could be accomplished trivially with "lift".

And this ignores interacting with any other transformer library!
Anyone who uses MonadPrompt along with another transformer (like
DatabaseT in the PostgreSQL library) needs to write any instances they
care about themselves, which adds to the difficulty in using the
libraries together.

Of course, the point of this message isn't just to complain.  The
overlap implementation was abhorrent and it *is* better now than it
was before.  But perhaps there is an abstraction we are missing that
would allow for better interoperability.  For example, the
type-compose library documentation at
http://haskell.org/haskellwiki/TypeCompose mentions that (f :. g) is
an applicative functor if both f and g are applicative functors, which
means there is a generic "transformer" for all applicative functors!
The presense of >>=/join for monads make this more difficult, although
there is the "product" definition:

> newtype Product m n a = Prod { runProd :: m (Either a (Product n m a)) }

which handles nesting joins by just nesting the monads recursively.
But in this case it is up to the user to figure out how to untangle
the spaghetti created, so that's no good.

So, does anyone have any good ideas for improving the interoperability of MTL?

  -- ryan


More information about the Haskell-Cafe mailing list