The author tries to say that the composition of any two monads isn't possible. This isn't because language is bad, but because there are monads whose composition isn't a monad.
For instance, IsntMonad
isn't a monad:
newtype IsntMonad a = IsntMonad (Maybe (IO a))
instance Monad IsntMonad where
m >>= k = ???
But, IsMonad
is a monad:
newtype IsMonad a = IsMonad { runIsMonad :: IO (Maybe a) }
instance Monad IsMonad where
(IsMonad ioma) >>= k = IsMonad $ do
ma <- ioma
case ma of
Just a -> runIsMonad $ k a
Nothing -> return Nothing
So, monad transformers it's just a way to make possibility of composition. But how it works, if we know that it isn't possible in general case? Yes, it's not possible for any two monads, but it's possible for some concrete monad and any other monad.
So, there are monads which can be compose with any other monad. Such monads in the first approximation are monad transformers.
For instance, the monad Maybe
can be compose with any other monad like this: Monad m => m (Maybe a)
, so m (Maybe a)
is a monad. But how we can do instance of monad for it?
instance Monad m => Monad ??? where ...
Then MaybeT
appears as helper, it is called a monad transformer (the suffix T
hints on that).
newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
instance Monad m => Monad (MaybeT m) where
m >>= k = MaybeT $ do
ma <- runMaybeT m
case ma of
Just a -> runMaybeT $ k a
Nothing -> return Nothing
After that, automatically MaybeT IO
, MaybeT []
, MaybeT STM
... are monads. You don't need to write instances for them. This is what they are good for.
We understood what monad transformers are just way to composition of monads. But it isn't an answer on: What is the composition of monads good for? Why did we spend our time and energy on finding a way to combine monads?
Well, as you known, any monad associated with some sort of effects. If we compose two monads we also compose them effects and this result we will be get automatically.
For example:
StateT s Maybe a -- a computation with a state `s` which can fail
ReaderT e Maybe a -- a computation with access to an environment `e` and option to fail
ReaderT e (StateT s Maybe) a -- a computation with state `s`, access to an environment `e` and option to fail
...
Composition of monads isn't commutative. For example, MaybeT (State s)
isn't same thing as StateT s Maybe
. A simple way to understand what effects will be produced by the composition is to remember that effects are produced from the inner monad to the outer monad. MaybeT (State s)
the first effect will be produced by State s
monad then by Maybe
. But in StateT s Maybe
the first effect will be produced by Maybe
then by State s
. This means that in cases of failure in StateT s Maybe
, we lose state, but inMaybeT (State s)
we save the state in which the error occurred.
So, the composition of monads is a simple way to build more complex monads just on type level.
How can this be applied in practice? For example, let's image situation. You have something and options to transform it. Imperative code can look like this:
thing = defaultValue;
if (transformByOption0)
doTransformationByOption0(thing);
if (transformByOption1)
doTransformationByOption1(thing);
...
In Haskell we can write:
thing <- flip execStateT defaultValue $ do
when transformByOption0 $
modify doTransformationByOption0
when transformByOption1 $
modify doTransformationByOption1
when transformByOption2 $
put =<< doMonadicTransformationByOption2 =<< get
...
What happend here? I locally wrapped some of my monad (it could be any) a monad State MyThing
so that it would be easy and simple to solve this problem. It's just a one from billion examples where composition of monads can easily solve your problems.