Monadology is intended as a collection of the best ideas in monad-related classes and types, with a focus on correctness and elegance, and theoretical understanding, rather than practical performance. I am interested in hearing further ideas, so at least initially expect a lot of change version-to-version.

## Re-exported Transformers

Monadology is built on the existing transformers package. It re-exports most of it. (It does *not* re-export `ListT`

).

## Result

This general-purpose “result” monad represents either success or failure, of any type. This sort of thing is so useful it could have been in base, but it isn’t.

```
data Result e a
= SuccessResult a
| FailureResult e
```

Of course, it’s isomorphic to `Either`

. But whereas `Either`

has a more general-purpose “symmetrical” feel, `Result`

is more intelligible to the reader as a monad.

## Exceptions

Monadology makes two separate approaches to exceptions: *one type* and *many types*. For example, for the `IO`

monad, there are many different exception types that can be both thrown and caught. But there is also the one type `SomeException`

that represents *all* the possible exceptions.

### Many Types

For the many-types approach, Monadology simply provides `MonadThrow`

and `MonadCatch`

classes, along with various functions:

```
class Monad m => MonadThrow e m where
throw :: forall a. e -> m a
class MonadThrow e m => MonadCatch e m where
catch :: forall a. m a -> (e -> m a) -> m a
```

### One Type

In principle, every monad `m`

has a single type of all the exceptions it can throw and catch. For this approach, this type is named `Exc m`

:

```
class Monad m => MonadException m where
type Exc m :: Type
throwExc :: Exc m -> m a
catchExc :: m a -> (Exc m -> m a) -> m a
type Exc Identity = Void
type Exc ((->) r) = Void
type Exc Maybe = ()
type Exc (Result e) = e
type Exc (ExceptT e m) = Either e (Exc m)
type Exc (StateT s m) = Exc m
type Exc IO = SomeException
```

Functions such as `finally`

and `bracket`

, that make no reference to any particular exception type, make use of this to ensure that they work for all exceptions that can be thrown.

## Composing Monads

You can compose two functors to get a functor. And you can compose two applicative functors to get an applicative functor. But, famously, you cannot compose two monads to get a monad.

At least, you cannot in general. But you *can*, of course, in certain cases. And we can capture the most useful cases by specifying the constraints we need on one of the monads so as to leave the other unconstrained.

### Inner Monad

`MonadInner`

is just the right constraint on the inner monad so as to compose with any outer monad to get a monad.

```
class (Traversable m, Monad m) => MonadInner m where
retrieveInner :: forall a. m a -> Result (m Void) a
newtype ComposeInner inner outer a = MkComposeInner (outer (inner a))
instance (MonadInner inner, Monad outer) => Monad (ComposeInner inner outer)
instance MonadInner inner => MonadTrans (ComposeInner inner)
```

Essentially, `inner a`

must be isomorphic to `Either P (Q,a)`

for some `P`

, `Q`

. If you examine the structure of the `WriterT`

, `ExceptT`

, and `MaybeT`

monad transformers, you’ll see that they are cases of this composition pattern.

### Outer Monad

`MonadOuter`

is just the right constraint on the outer monad so as to compose with any inner monad to get a monad.

```
newtype WExtract m = MkWExtract (forall a. m a -> a)
class Monad m => MonadOuter m where
getExtract :: m (WExtract m)
newtype ComposeOuter outer inner a = MkComposeOuter (outer (inner a))
instance (MonadOuter outer, Monad inner) => Monad (ComposeOuter outer inner)
instance MonadOuter outer => MonadTrans (ComposeOuter outer)
```

Essentially, `outer a`

must be isomorphic to `P -> a`

for some `P`

. If you examine the structure of the `ReaderT`

monad transformer, you’ll see that it’s a case of this composition pattern.

## Lifecycles

`LifecycleT`

is a monad transformer for managing the closing of opened resources, such as file handles, database sessions, GUI windows, and the like. You can think of it as a conceptually simpler version of `ResourceT`

.

The actual code is slightly different in the contents of the `MVar`

, but it basically looks like this:

```
newtype LifecycleT m a = MkLifecycleT (MVar (IO ()) -> m a)
runLifecycle :: (MonadException m, MonadTunnelIO m) => LifecycleT m a -> m a
lifecycleOnClose :: MonadAskUnliftIO m => m () -> LifecycleT m ()
type Lifecycle = LifecycleT IO -- the most common usage
```

That `MVar`

simply stores all the “close” operations to be run at the end of each “lifecycle” when called by `runLifecycle`

, in reverse order of their opening. You can add your own close operations with `lifecycleOnClose`

.

Of course you may be thinking, what if I want to close things in a different order? For example, GUI windows get closed when the close box is clicked, not in the reverse order of opening.

For this you want to get a closer function:

`lifecycleGetCloser :: MonadIO m => LifecycleT m a -> LifecycleT m (a, IO ())`

For example,

```
newGUIWindow :: Lifecycle Window
makeMyWindow :: Lifecycle Window
makeMyWindow = do
(window,closer) <- lifecycleGetCloser newGUIWindow
lift $ onCloseBoxClicked window closer
return window
```

Here, `closer`

is an idempotent operation that will call the closer of `newGUIWindow`

, that is, to close the window. Subsequent calls do nothing. It also gets called at the end of the lifecycle, to ensure that the window is eventually closed if it hasn’t been already.

Also, you may come across certain functions that make use of the “with” pattern, to manage opening and closing. Here are a couple from the base library:

```
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
withBinaryFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
```

Monadology is capable of “unpicking” this pattern coroutine-style, and converting it to a `Lifecycle`

:

```
lifecycleWith :: (forall r. (a -> IO r) -> IO r) -> Lifecycle a
fileHandle :: FilePath -> IOMode -> Lifecycle Handle
fileHandle path mode = lifecycleWith $ withFile path mode
```

## Coroutines

Speaking of coroutines, Monadology has a class for that.

```
class Monad m => MonadCoroutine m where
coroutineSuspend :: ((p -> m q) -> m r) -> CoroutineT p q m r
```

This is experimental, as the only useful instances I’ve come across are monads based on `IO`

, which supports coroutines by using threads.

The `CoroutineT`

transformer is a special case of the `StepT`

transformer, which is for step-by-step execution.

## Transitive Constraints

For many transformers, certain constraints on a monad are transitive to the transformed monad. For example:

```
Monad m => Monad (ReaderT r m)
(MonadPlus m, Monoid w) => MonadPlus (WriterT w m)
MonadIO m => MonadIO (ExceptT m)
```

Monadology has a class for this:

```
class TransConstraint c t where
hasTransConstraint :: forall m. c m => Dict (c (t m))
instance TransConstraint Monad (ReaderT r)
instance Monoid w => TransConstraint MonadPlus (WriterT w)
instance TransConstraint MonadIO ExceptT
```

Why not just use GHC’s `QuantifiedConstraints`

extension? Because GHC has issues satisfying quantified constraints. So there’s an explicit class instead.

## Tunnelling, Hoisting and Commuting

Tunnelling allows you to manipulate monads underneath a transformer. Each tunnellable transformer is associated with a *tunnel monad*, that represents the “effect” of the transformer.

```
type p --> q = forall a. p a -> q a
class (MonadTrans t, TransConstraint Monad t) => MonadTransHoist t where
hoist :: forall m1 m2. (Monad m1, Monad m2) =>
(m1 --> m2) -> t m1 --> t m2
class (MonadTransHoist t, MonadInner (Tunnel t)) => MonadTransTunnel t where
type Tunnel t :: Type -> Type
tunnel :: forall m r. Monad m =>
((forall m1 a. Monad m1 => t m1 a -> m1 (Tunnel t a)) -> m (Tunnel t r)) -> t m r
```

Tunnel monads are, curiously enough, always instances of the aforementioned `MonadInner`

. For example:

```
type Tunnel (ReaderT s) = Identity
type Tunnel (WriterT w) = (,) w
type Tunnel (StateT s) = (,) (Endo s)
type Tunnel MaybeT = Maybe
type Tunnel (ExceptT e) = Either e
type Tunnel (ComposeInner inner) = inner
type Tunnel (ComposeOuter outer) = Identity
```

(This is essentially a correction and generalisation of `MonadTransControl`

.)

It’s straightforward to derive hoisting from tunnelling, which is why `MonadTransHoist`

is a superclass of `MonadTransTunnel`

. And furthermore, you can commute two transformers in a stack, if you can commute their tunnel monads (which you always can).

```
commuteTWith :: (MonadTransTunnel ta, MonadTransTunnel tb, Monad m) =>
(forall r. Tunnel tb (Tunnel ta r) -> Tunnel ta (Tunnel tb r)) ->
ta (tb m) --> tb (ta m)
commuteInner :: (MonadInner m, Applicative f) => m (f a) -> f (m a)
commuteT :: (MonadTransTunnel ta, MonadTransTunnel tb, Monad m) =>
ta (tb m) --> tb (ta m)
commuteT = commuteTWith commuteInner
```

## Unlifting

Monadology has two classes for unlifting transformers.

```
type Unlift c t = forall m. c m => t m --> m
newtype WUnlift c t = MkWUnlift (Unlift c t)
class (...) => MonadTransUnlift t where
-- | lift with an unlifting function that accounts for the transformer's effects (using MVars where necessary)
liftWithUnlift :: forall m r. MonadIO m =>
(Unlift MonadTunnelIOInner t -> m r) -> t m r
-- | return an unlifting function that discards the transformer's effects (such as state change or output)
getDiscardingUnlift :: forall m. Monad m =>
t m (WUnlift MonadTunnelIOInner t)
-- | A transformer that has no effects (such as state change or output)
class MonadTransUnlift t => MonadTransAskUnlift t where
askUnlift :: forall m. Monad m => t m (WUnlift Monad t)
```

Only `ReaderT`

(and `IdentityT`

) and the like can be instances of the more restrictive `MonadTransAskUnlift`

.

However, `MonadTransUnlift`

also has instances for `StateT`

and `WriterT`

. These allow correct unlifting without discarding effects (though another function is provided if you want discarding). How is this possible? ~~Magic!~~ `MVar`

s! Unlifting `StateT`

simply holds the state in an `MVar`

. Unlifting `WriterT`

uses an `MVar`

to collect effects at the end of each unlift.

Using `MVar`

s also makes everything thread-safe. Here’s an example:

```
longComputation1 :: IO ()
longComputation2 :: IO ()
ex :: StateT Int IO ()
ex = liftWithUnlift $ \unlift -> do
a <- async $ do
longComputation1
unlift $ modify succ
longComputation2
unlift $ modify succ
wait a
```

Here, `longComputation1`

and `longComputation2`

can run in parallel, in different threads. But `unlift`

forces synchronisation, meaning that the `modify`

statements never overlap. Instead, state is passed from one to the other. So `ex`

is guaranteed to add two to its state.

As mentioned earlier, the tunnel monads of transformers in `MonadTransTunnel`

are all instances of `MonadInner`

. But if the transformer is an instance of `MonadTransUnlift`

, its tunnel monad will be an instance of the stricter class `MonadExtract`

. And if the transformer is an instance of `MonadTransAskUnlift`

, then its tunnel monad will be an instance of `MonadIdentity`

, monads equivalent to the identity monad.

## The Same, but Monads Relative to IO

Often a monad can be understood as some transformer over `IO`

. In such a case, we might want to know the properties of that transfomer.

Monadology provides classes for such monads, that mirror classes for transformers:

`MonadTrans` | `MonadIO` |

`MonadTransHoist` | `MonadHoistIO` |

`MonadTransTunnel` | `MonadTunnelIO` |

`MonadTransUnlift` | `MonadUnliftIO` |

`MonadTransAskUnlift` | `MonadAskUnliftIO` |

## Composing and Stacking Transformers

The `ComposeT`

transformer allows you to compose monad transformers (unlike composing monads, there is no restriction on this). Generally speaking, if `t1`

and `t2`

both have some property, then `ComposeT t1 t2`

will have it too.

The `StackT`

transformer allows you to deal with whole stacks of transformers, parameterized by a list of their types:

```
type TransKind = (Type -> Type) -> (Type -> Type)
type StackT :: [TransKind] -> TransKind
newtype StackT tt m a = MkStackT (ApplyStack tt m a)
type ApplyStack :: forall k. [k -> k] -> k -> k
type family ApplyStack f a where
ApplyStack '[] a = a
ApplyStack (t ': tt) a = t (ApplyStack tt a)
```

## Monad Data

The concepts of “reader”, “writer”, and “state” monads each imply a kind of data: readers have *parameters*, writers have *products*, and states have *references*. And pretty much any monad has *exceptions*. So, why not make that data explicit, so we can manipulate it directly?

```
data Param m a = MkParam
{ paramAsk :: m a
, paramWith :: a -> m --> m
}
readerParam :: Monad m => Param (ReaderT r m) r
data Ref m a = MkRef
{ refGet :: m a
, refPut :: a -> m ()
}
stateRef :: Monad m => Ref (StateT s m) s
data Prod m a = MkProd
{ prodTell :: a -> m ()
, prodListen :: forall r. m r -> m (r, a)
}
writerProd :: Monad m => Prod (WriterT w m) w
data Exn m e = MkExn
{ exnThrow :: forall a. e -> m a
, exnCatch :: forall a. m a -> (e -> m a) -> m a
}
allExn :: forall m. MonadException m => Exn m (Exc m)
someExn :: forall e m. MonadCatch e m => Exn m e
```

Parameters and references can be mapped by lenses. Not so much products, though there is one thing we can do with them.

```
mapParam :: Functor m => Lens' a b -> Param m a -> Param m b
mapRef :: Monad m => Lens' a b -> Ref m a -> Ref m b
foldProd :: (Applicative f, Foldable f, Applicative m) => Prod m a -> Prod m (f a)
```

Of course, other monads have their own references:

```
ioRef :: IORef a -> Ref IO a
stRef :: STRef s a -> Ref (ST s) a
```

## Odd Stuff

`ReaderStateT`

and `TransformT`

are odd things that I make use of elsewhere, but don’t really understand. Both of them convert `Param`

s into `Ref`

s.

```
newtype WRaised f m = MkWRaised (forall a. f a -> m a)
type ReaderStateT f m = StateT (WRaised f m) m
readerStateParamRef :: Monad m => Param f a -> Ref (ReaderStateT f m) a
newtype TransformT m a = MkTransformT (forall r. (a -> m r) -> m r)
transformParamRef :: Monad m => Param m a -> Ref (TransformT m) a
```

## Not Included

`ListT`

. This does not transform every monad to a monad, so is not a monad transformer.- Any notion of a “base” monad. While every transformer stack must logically have some base monad, the concept is non-parametric as transformed monads cannot be base monads.
- Lifted “batteries” functions. Just use
`lift`

. - An effect system.

## And also…

I have substantially expanded, cleaned up and reorganised `witness`

, my package for type witnesses, which Monadology makes use of. I have also published `type-rig`

, which provides the `Summable`

and `Productable`

classes used for monad data.

â€” Ashley Yakeley