packages feed

monad-levels-0.1.0.0: README.md

`monad-levels`
==============

Why not mtl?
------------

The oft-spouted problem with the standard monad transformer library
[mtl] and similar libraries is that instances are quadratic: you need
a separate instance for each valid combination of transformer +
typeclass.

[mtl]: http://hackage.haskell.org/package/mtl

For end users, this isn't really a problem: after all, all the
required instances have already been written for you!

But what happens if you have a custom transformer, or a custom
typeclass?

What about if you want to have something like `MonadIO` but for a
different base monad?

Then you need to write all those extra instances.

What makes it more frustrating is that many of the instance
definitions are identical: typically for every transformer (using
`StateT s m` as an example) it becomes a matter of:

* Possibly unwrap the transformer from a monadic value to get the
  lower monad (e.g. `StateT s m a -> m (a,s)`);

* Possibly add internal values (e.g. `m a -> m (a,s)`);

* Wrap the lower monad from the result of the computation back up into
  the transformer (e.g. `m (a,s) -> StateT s m a`).

The solution
------------

Ideally, instead we'd have something along the lines of (simplified):

```haskell

class (Monad m) => MonadBase m where
  type BaseMonad m :: * -> *

  liftBase :: BaseMonad m a -> m a

class (MonadBase m) => MonadLevel m where

  type LowerMonad m :: * -> *

  type InnerValue m a :: *

  -- A continuation-based approach for how to lift/lower a monadic value.
  wrap :: (    (m a -> LowerMonad m (InnerValue m a)             -- unwrap
            -> (LowerMonad m a -> LowerMonad m (InnerValue m a)) -- addInternal
            -> LowerMonad m (InnerValue m a)
          )
          -> m a
```

With these two classes, we could then use Advanced Type Hackery (TM)
to let us instead just specify instances for the transformers/monads
that *do* have direct implementations for a typeclass, and then have
the rest defined for us!

It turns out that this approach is even powerful enough to make
`liftBase` redundant, and it isn't limited to just lifting a monad but
can instead be used for arbitrary functions.

Advantages
----------

* Minimal specification required for adding new typeclasses: just
  specify the instances for monads that satisfy it, and then use the
  provided machinery to lift/lower methods to other transformers in
  the monadic stack.

* Works even for polyvariadic functions.

* Still allows specifying whether certain transformers do _not_ allow
  some constraints to pass through (e.g. `ContT` does not allow access
  to a `WriterT`).

Disadvantages
-------------

* Requires a _lot_ of GHC extensions.

* Requires usage of proxies when lifting/lowering typeclass methods.

* Large usage of associated types means type errors can be difficult
  to decipher.

* Due to usage of closed type-families, it is not possible to add
  extra instances to typeclasses (i.e. it is not possible to use a
  custom `State` monad/monad-transformer with
  `Control.Monad.Levels.State`).

* Currently un-benchmarked; as such, it's not known how much of a
  performance penalty this approach takes.

* Lowering polyvariadic functions requires specifying the type of the
  function using a specific grammar (though the common `m a -> m a`
  case is pre-defined).