# Heavy lifting

I'm trying to improve my skills with monad stacks. Till now, I've got
away with quite a lot, but not *grokked* what I was doing. (Sorry, I'm
British, and we double consonants before an *-ed* or *-ing* suffix, if
the vowel is short.)

Here's some simple code:

module Main where import Control.Monad.Reader main = do putStrLn "Hello, world!" runReaderT mything 5 mything :: ReaderT Int IO () mything = do i <- ask lift $ putStrLn $ "i == " ++ show i

Of course `main` is in the `IO` monad; `mything` is in a stack
consisting of `ReaderT` wrapped around `IO`. The interesting part
is that we can use `lift` to run an action in the inner monad.

The definition of `lift` looks like this:

lift :: MonadTrans t => forall m a. Monad m => m a -> t m a

Lift a computation from the argument monad to the constructed monad.

Now, I'm a bit vague about what `forall` means there, but lets not
worry about that now. Clearly here the "argument monad" is `IO`, and
the "constructed monad" is `ReaderT`. It's the computation itself that
is "lift"ed:

+---------+ | ReaderT | ^ ^ +---------+ | | lift | IO | | putStrLn ... | +---------+

OK, so let's look at monad-control. This offers the `control` function
which can be used like so:

module Main where import Control.Monad.Reader import Control.Monad.Trans.Control main = do putStrLn "Hello, world!" runReaderT mything 5 mything :: ReaderT Int IO () mything = do i <- ask control $ \run -> do putStrLn $ "1. i == " ++ show i run $ do j <- ask lift $ putStrLn $ "2. j == " ++ show j lift $ putStrLn $ "3. i == " ++ show i

Within the `control` block, we are in `IO` (and hence can use
`putStrLn` directly). The `run` function that is constructed by
`control` gives us an escape hatch back to `ReaderT` (and, as you'd
expect, once we're back in `ReaderT` we can use `lift` again for
`IO` actions).

Now, it would be nice to split the control block off into a separate
function, to get a better idea of the types involved. To make that work,
though, we have to give it a fairly hairy type signature involving
RunInBase. That utilizes rank *N* types, so we also need the relevant
extension:

{-# LANGUAGE RankNTypes #-} module Main where import Control.Monad.Reader import Control.Monad.Trans.Control main = do putStrLn "Hello, world!" runReaderT mything 5 mything :: ReaderT Int IO () mything = do i <- ask lift $ putStrLn $ "1. i == " ++ show i control otherthing lift $ putStrLn $ "4. i == " ++ show i otherthing :: RunInBase (ReaderT Int IO) IO -> IO (StM (ReaderT Int IO) ()) otherthing run = do putStrLn $ "2. in IO" run $ do j <- ask lift $ putStrLn $ "3. j == " ++ show j

In passing, it is curious that in the previous example GHC was happy to
*use* a rank *N* type without the extension; it is only if we need to
*name* it that the extension is required.

Anyway, let's liven things up again. Here's some code with a more exotic monad stack:

module Main where import Control.Monad.Reader import Control.Monad.Writer main = do putStrLn "Hello, world!" cnt <- execWriterT $ runReaderT mything 5 putStrLn $ "cnt is " ++ show (getSum cnt) mything :: ReaderT Integer (WriterT (Sum Integer) IO) () mything = do i <- ask lift $ tell $ Sum 1 tell $ Sum 1 lift $ lift $ putStrLn $ "i is " ++ show i liftIO $ putStrLn $ "i is " ++ show i

I've used stacks like this before. It's always looked to me that in the
function call `ReaderT` is on the "inside", whereas in the type it's
on the "outside". After all, if I write `Right $ Just 5` I get a value
of type `Either a (Maybe Int)`. Anyway, for reasons that elude me at
the moment, with monads it's different. Each new monadic environment
adds a new monad to the top of the stack, and the left of the type

This example demonstrates some further wrinkles. We can simply use
`ask` from the `ReaderT` monad of coures. And we can lift `tell`
from `WriterT`, but we can also write a plain `tell`, apparently
unlifted. (There's some kind of magic going on under the hood that I
don't understand to make this work.)

To run a computation in `IO`, we can, as you might expect, lift it
twice. But as an alternative we can simply use `liftIO` which plucks
`IO` from the bottom of any monadic stack in a single step, no matter
how deep the stack. (Again, this is magic as far as my current level of
understanding is concerned.)

Using monad-control with this more complex stack is not significantly
different from the earlier example. I've used a type alias to stop the
type of `otherthing` running off the side of the screen:

{-# LANGUAGE RankNTypes #-} module Main where import Control.Monad.Reader import Control.Monad.Trans.Control import Control.Monad.Writer main = do putStrLn "Hello, world!" cnt <- execWriterT $ runReaderT mything 5 putStrLn $ "cnt is " ++ show (getSum cnt) type Stack = ReaderT Integer (WriterT (Sum Integer) IO) mything :: Stack () mything = do i <- ask tell $ Sum 1 control otherthing liftIO $ putStrLn $ "i is " ++ show i otherthing :: RunInBase (Stack) IO -> IO (StM Stack ()) otherthing run = do putStrLn "in IO here" run $ do j <- ask liftIO $ putStrLn $ "j is " ++ show j tell $ Sum 1

I hope somebody out there finds these little examples useful. My style
of learning revolves around lots and lots of tiny examples; each one
(hopefully!) understood thoroughly. This predilection seems to put me in
a minority of Haskell programmers; so many packages on hackage have not
a single example. Yes, in theory I can work out how things fit together
from the types, but I often feel that I'm struggling to reverse engineer
how a package is *intended* to be used.

I like instant rewards, and it's nice to start with an example that will actually compile. (The alternative, starting by bouncing back and forth between a bunch of error messages and the package documentation, is much less appealing.) Once I have something that will build, I can continuously deform it till it does what I want. If any small step introduces an error, then I can focus on what I did wrong in that step.

Anyway, as a result of putting together this blog post, I have improved
my understanding of monad stacks considerably. Although it's not reached
the level of *grokking* yet, I was able to generalize `runTCPClient ::
ClientSettings -> (AppData -> IO a) -> IO a` and `runTCPServer ::
ServerSettings -> (AppData -> IO ()) -> IO ()` from the
**streaming-commons** package to work with any monad `m` for which
`(MonadIO m, MonadBaseControl IO m) => m`.