Version 5 (modified by ross, 4 years ago)

--

Add transformers and revise the mtl package to depend on it

The proposal is that version 2 of the mtl package be the current contents of the monads-fd package, which depends on the transformers package.

Proposer

  • Ross Paterson (maintainer of transformers and monads-fd)

If this proposal is accepted, monads-fd would be obsoleted and transformers would be turned over to community control (like the current mtl package).

Rationale

Monad transformers are widely used, but the MTL implementation is tied to functional dependencies, whose future is in doubt. Many people want to try out versions based on type functions, or without using advanced type classes at all, but their interfaces will then be incompatible with libraries using mtl.

The idea is to factor out the monad transformers as a Haskell 98 package, which can be used by itself or with type classes based on either functional dependencies or type functions. Interfaces referring to the monad transformers would be compatible across the different libraries.

Structure

The current mtl is to be split in two:

  • transformers is a Haskell 98 package containing the identity monad (Data.Functor.Identity), 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 Control.Monad.Trans.Class documentation for examples), or with packages adding type classes.
  • mtl-2 (the current monads-fd) depends on transformers and adds type classes using functional dependencies. Usage is very close to mtl-1, 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 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)
    
    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 State s and StateT s Identity and ensuring that they are consistent.

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.

Transition issues

In early September 2010, there were 694 packages in hackage that directly depended on mtl. Of these,

  • 385 built unchanged with the proposed mtl-2
  • 18 had a bounded mtl dependency that excluded mtl-2
  • 2 failed because of the recent move of the Monad (Either e) instance from mtl to base
  • 43 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
    • 1 that defined an overlapping Error instance (hssqlppp)
    • 18 that used the constructors of base monads; in 16 of these (except jmacro and yhccore) the fixes are trivial
  • 248 failed for other reasons (e.g. failed anyway or depended on one of the other failures)

Attachments