[[PageOutline]]
= Add transformers and revise the mtl package to depend on it =
The proposal is that version 2 of the [http://hackage.haskell.org/package/mtl mtl] package be the current contents of the [http://hackage.haskell.org/package/monads-fd monads-fd] package, which depends on the [http://hackage.haskell.org/package/transformers transformers] package.
== Proposer ==
* Ross Paterson (maintainer of transformers and monads-fd)
If this proposal is accepted, monads-fd would be obsoleted (hidden from the hackage package list) and transformers would be turned over to community control (like the current mtl package).
== Rationale ==
Monad transformers are widely used, but the MTL interface is tied to functional dependencies, whose future is in doubt. Many people want to try out interfaces based on type functions, or without using experimental type class features, but their interfaces will then be incompatible with libraries using mtl.
The idea is to factor out the monad transformers as the Haskell 98 package. The objectives for this package are:
1. that it be as useful as possible by itself, so that it can replace many uses of mtl.
2. that it serve as a base for other packages adding type classes based on either functional dependencies (e.g. mtl) or type functions, with interfaces referring to the monad transformers being compatible across the different libraries.
The [http://hackage.haskell.org/package/transformers transformers] and [http://hackage.haskell.org/package/monads-fd monads-fd] packages have been in use for over 18 months, and are used by [http://sparky.haskell.org:8080/package/transformers/reverse 112] and [http://sparky.haskell.org:8080/package/monads-fd/reverse 23] packages respectively. The split structure has been shown to be viable, but its existence in parallel with mtl has meant extra effort for authors of client packages. It is time to complete the transition.
== Structure ==
The current mtl is to be split in two:
* [http://hackage.haskell.org/package/transformers transformers] is a Haskell 98 package containing
* base functors (Data.Functor.Constant and Data.Functor.Identity),
* operations on functors (Data.Functor.Compose and Data.Functor.Product),
* transformer classes (Control.Monad.Trans.Class and Control.Monad.IO.Class) and
* concrete monad transformers with code to lift operators (Control.Monad.Trans.*).
The package can be used on its own (see the [http://hackage.haskell.org/packages/archive/transformers/latest/doc/html/Control-Monad-Trans-Class.html Control.Monad.Trans.Class documentation] for examples), or with packages adding type classes.
* mtl-2 (the current [http://hackage.haskell.org/package/monads-fd monads-fd]) depends on transformers and adds type classes using functional dependencies. It has the same modules as mtl-1 and usage is very close, except for the differences listed below.
== Incompatibilities ==
The proposed interface of mtl-2 is close to that of mtl-1, but with the following differences (illustrated with Reader):
* instances of {{{Applicative}}} and {{{Alternative}}} have been added as appropriate, e.g.
{{{
instance (Applicative m) => Applicative (ReaderT r m) where ...
instance (Alternative m) => Alternative (ReaderT r m) where ...
}}}
''Rationale:'' These classes postdate the MTL, and such instances have been repeatedly defined in various packages. They belong together with the type constructors.
* Functor instances for monad transformers no longer require Monad where Functor is sufficient. Unfortunately this is incompatible because Functor is not a superclass of Monad, e.g.
{{{
instance (Monad m) => Functor (ReaderT r m) where ...
}}}
is replaced by
{{{
instance (Functor m) => Functor (ReaderT r m) where ...
}}}
''Rationale:'' These instances are more general, and are consistent with the instances of other classes.
* simple monads are now aliases for monad trasformers applied to {{{Identity}}}, e.g.
{{{
newtype Reader r a = Reader { runReader :: r -> a }
}}}
is replaced by
{{{
type Reader r = ReaderT r Identity
reader :: (r -> a) -> Reader r a
reader f = ReaderT (Identity . f)
runReader :: Reader r a -> r -> a
runReader m = runIdentity . runReaderT m
}}}
''Rationale:'' This avoids repetition in the interfaces of both transformers and the proposed mtl-2. It makes transformers more useful on its own, and also saves clients of mtl from defining instances for both {{{Reader r}}} and {{{ReaderT r}}} and ensuring that they are consistent.
* The instance {{{Error String}}} is restructured to avoid a type synonym instance:
{{{
instance Error String where
noMsg = ""
strMsg = id
}}}
is replaced by
{{{
instance ErrorList a => Error [a] where
strMsg = listMsg
class ErrorList a where
listMsg :: String -> [a]
instance ErrorList Char where
listMsg = id
}}}
''Rationale:'' This makes the instance Haskell 98, so it can be included in the transformers package.
== Transition issues ==
In early September 2010, of the 510 buildable packages in hackage that directly depended on mtl ([http://sparky.haskell.org:8080/package/mtl/reverse all uses]),
* 312 built unchanged with the proposed mtl-2
* 102 had a bounded mtl dependency (or a dependent package had) that excluded mtl-2
* 2 failed because of the recent move of the Monad (Either e) instance from mtl to base
* 41 failed with the new mtl:
* 11 because they defined their own Applicative instances (which can now be deleted)
* 11 because of the changed constraint on Functor instances
* 15 that used the constructors of base monads (which can be trivially replaced)
* 3 that defined instances for base monads
* 1 that defined an overlapping Error instance
* 53 failed because they depended on one of the other failures
The attached test report lists the packages involved.
=== Upgrading to mtl-2 ===
Most packages build unchanged with mtl-2. Most of the remainder require only small changes to upgrade to it, though that will usually render them incompatible with mtl-1 without some CPP directives. Here are the fixes for common messages:
''Illegal instance declaration for Applicative (State a)''
This usually means that the instance you're defining is now defined in the {{{transformers}}} package. You may wish to check it's the same, so you can just delete it.
''Could not deduce (Functor m) from the context (Monad m)[[BR]]arising from a use of fmap''
This will be a situation where you're using a monad constructed with a transformer as a Functor. You can replace {{{fmap}}} with {{{liftM}}} and the code will build with old and new versions of mtl.
''Could not deduce (Functor m) from the context (Monad m)[[BR]]arising from the superclasses of an instance declaration''
This will be another situation where you're using a monad constructed with a transformer as a Functor. You can replace the {{{Monad m}}} with {{{Functor m}}} to make it work with mtl-2, or add a {{{Functor m}}} constraint to make it work with old and new versions of mtl.
''Not in scope: data constructor State''
The {{{State}}} type is now a type synonym. You could replace the {{{State}}} data constructor with the {{{state}}} function.
''Not in scope: runState''
You probably imported {{{State(..)}}}, which won't work now that {{{State}}} is a type synonym. You need to import {{{State}}} and {{{runState}}}. (That will work with old versions of mtl too.)
''Illegal instance declaration for Myclass (State Foo)''
If you have a matching instance for {{{StateT}}}, you can delete the instance for {{{State}}}. Otherwise you need to generalize your instance to {{{StateT}}}. If that's not possible, you may need to introduce a newtype.
== Issues raised in the discussion ==
* Is there any performance penalty for defining the base monads as special cases of monad transformers?
The two should generate the same code ([http://www.haskell.org/pipermail/libraries/2010-September/014323.html example]).
* The {{{transformers}}} package provides a function {{{state}}} as a replacement for the removed data constructors {{{State}}} when used as a function (and similarly for the others). Should mtl re-export those?
== Other issues ==
Some other issues have been raised with mtl over the years, but they are orthogonal to this proposal:
* The {{{MonadCont}}} instance for {{{StateT}}} is not compatible with the monad transformer. The transformers package provides the correct lifting (in which callcc causes the state to rollback on entering the saved continuation), but also provides the MTL lifting for compatibility, and this is used by monads-fd. It could be switched to the correct lifting later.
* The {{{ErrorT}}} monad transformer has an {{{Error}}} constraint, so that errors can be passed to the fail method of the Monad class.
* {{{ListT}}} only works on commutative monads.