After mastering monads, the next challenge we usually face is combining two (or more) separate monads. In this tutorial I outline simple plumbing (when the monads to be fit together are not that different), and two mainstream approaches for general plumbing.

## Simple plumbing

When the monads are similar in nature, we can convert one to the other in an ad-hoc, monad-specific way.
This mostly happens when chaining computations, some of which run in `Maybe`

, others in `Either`

.

Having the following functions,

```
data MyError = Err Text | OtherErr
verboseFailing :: Int -> Either MyError Int
verboseFailing = ...
justFailing :: Int -> Maybe Int
justFailing = ...
```

we can compose them to either result a `Maybe`

or an `Either`

, depending on our intent.
Usually it is a good idea to go with the latter, but let's see both.

```
import Control.Monad ((>=>))
import Control.Error.Util (note)
verboseComp :: Int -> Either MyError Int
verboseComp = verboseFailing >=> note OtherErr . justFailing
```

Here we take advantage of the `note`

function from the `errors`

package, but
you could write it yourself as well. The other way around:

```
import Control.Error.Util (hush)
justFailingComp :: Int -> Maybe Int
justFailingComp = hush . verboseFailing >=> justFailing
```

This time the `hush`

function was used to silence the error.
The `errors`

package contains a lot of combinators that for example let us go from `Maybe`

to `MaybeT`

easily. Check it out.

### Plumbing Eithers with different error types.

Once we are talking about plumbing and `Eithers`

- assume we have something like the following.

```
data LowlevelError = ...
data InputError = ...
validated :: Text -> Either InputError Int
validated = ...
someComp :: Int -> Either LowlevelError Int
someComp = ...
```

How do we combine these methods? An approach is to create a hierarchy of errors.

```
data AppError = ELowlevel LowlevelError | EInput InputError
app s = do
a <- EInput `fmapL` validated s
ELowlevel `fmapL` someComp a
```

The `fmapL`

is again from `errors`

, and maps on the left.
Although we could make it nicer by introducing a flipped alias.

```
wrapError = flip fmapL
app s = do
a <- validated s `wrapError` EInput
someComp a `wrapError` ELowlevel
```

## Combining wildly different monads

What happens when we leave the safe realm of `Maybe`

and `Either`

?

```
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
more :: Reader Int Int
more = ask >>= return . (+1)
bang :: State String Int
bang = modify (++"!") >> get >>= return . length
-- | How to combine more and bang?
morePlusBang = undefined
main = do
-- We can of course use them separately.
print $ runReader more 42
print $ runState (bang >> bang) "?"
```

Let me outline two mainstream approaches for plumbing different monads.

## Solution 1: Monadic interfaces

One solution is to program against monad interfaces from `mtl`

,
instead of actual instances from `transformers`

.

```
{-# START_FILE Lib.hs #-}
{-# LANGUAGE FlexibleContexts #-}
module Lib where
-- Notice we just import the monad interfaces,
-- not specific implementations.
import Control.Monad.Reader.Class
import Control.Monad.State.Class
-- | The interface of 'm' is specified using a typeclass now.
-- 'm' can be any data type, if it at least conforms to the
-- 'MonadReader' interface (which generalizes the various 'Reader's).
more :: (MonadReader Int m) => m Int
more = ask >>= return . (+1)
bang :: (MonadState String m) => m Int
bang = modify (++"!") >> get >>= return . length
-- | We need to propagate the merged requirements of the
-- parts we use.
morePlusBang :: (MonadReader Int m, MonadState String m) => m Int
morePlusBang = do
a <- more
b <- bang
return $ a + b
{-# START_FILE Main.hs #-}
import Control.Monad.Trans.RWS
import Lib
main = do
-- The type signature just fixes the unused 'w' monoid in RWS.
print (runRWS (bang >> morePlusBang) 42 "?" :: (Int,String,[()]))
```

Here we have chosen `RWS`

, the Reader-Writer-State monad,
as it supports our requirements well.

In fact, you are free to choose any monad (stack) that conforms to the requirements. For example

```
main = do
print $ runState (runReaderT (bang >> morePlusBang) 42) "?"
```

automatically infers the stack to be `ReaderT Int (State String) a`

.

The pro is that now we can nicely compose the computations, as long as we are able to find a single stack that fits all our requirements.

A cons is that the typeclasses might be more costy runtime-wise, as the
typeclass dictionaries need to be passed. Also, the signatures are a bit
more heavyweight due to the added constraints. Latter can be somewhat mitigated
by using the `ConstraintKinds`

language extension, which lets us do
aliases like `type MyReqs m = (MonadState String m, MonadReader Int m)`

.

### Baking the stack

It is a reported usage, that after fleshing out the exact requirements for a given application stack, a specific stack is "baked" in.

First, a new monad is created, and all typeclass constraints are replaced by using the specific monad.

```
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype App a = App { runApp :: ReaderT Int (State String) a }
deriving (Monad, MonadState, MonadReader)
more :: App Int
more = App $ ask >>= return . (+1)
bang :: App Int
bang = App $ modify (++"!") >> get >>= return . length
morePlusBang :: App Int
morePlusBang = do
a <- more
b <- bang
return $ a + b
```

The pro seems to be that this helps the compiler better inline?
While a con is, that now all functions in the app have access to the full
stack, while previously functions could only access the part they had
business with (`Reader`

for `more`

, `State`

for `bang`

).

Actually if no new functionality is added, `App`

could have been a simple
type alias instead of a newtype. A newtype makes more sense if we also provide
specialized operations on it, at least hiding the internals a bit.

## Solution 2: Transformer stacks

If we are not too picky, can just implement the functions via monad transformers, and plumb them together explicitly when needed.

Note that in the code now we need to `lift`

the inner monad to the outermost
level. If our stack were multiple levels deep, we might need `lift . lift`

and so on.

```
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State
more :: (Monad m) => ReaderT Int m Int
more = ask >>= return . (+1)
bang :: (Monad m) => StateT String m Int
bang = modify (++"!") >> get >>= return . length
morePlusBang :: ReaderT Int (State String) Int
morePlusBang = do
a <- more
b <- lift bang
return $ a + b
main = print $ runState (runReaderT morePlusBang 42) "?"
```

The pro is that it is straightforward, and most of the time we don't need to combine too many monads anyway. The cons is that we really need to have a good grasp of monad transformers to be able to lift at will. But the latter would be needed anyway. Also, our code is a bit less flexible, since altering the stack has effect on functions using it. For example, adding a new layer needs us to add some more lifting.

I suggest also taking a look at the `mmorph`

package, which can be
useful if we have to work with monad stacks not under our control:
It lets us to swap (`hoist`

) the inner monad layer in a transformer (among others).

## Outro

Hopefully this post gives some pointers to you. In the real world, nothing is ultimately black and white, and you are encouraged to do your own contemplation about the various approaches.