# monad-levels

Specific levels of monad transformers https://github.com/ivan-m/monad-levels

Latest on Hackage: | 0.1.0.1 |

This package is not currently in any snapshots. If you're interested in using it, we recommend adding it to Stackage Nightly. Doing so will make builds more reliable, and allow stackage.org to host generated Haddocks.

**Ivan Lazar Miljenovic**

**Ivan.Miljenovic@gmail.com**

`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.

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):

```
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).

## Changes

## 0.1.0.1

Add compatibility for transformers-0.3

## 0.1.0.0

- Initial release