Anatomy of a monad transformer

haskell, monad-transformers, monads

Solution

`mtl` package doesn't implement monad transformers. At least WriterT is just reexported from `transformers`.

`transformers` package implements `WriterT`, which is a monad transformer itself. `Writer` is just an alias:

type Writer w = WriterT w Identity

Some libraries can implement `Writer` separately, but anyway it is just a special case of `WriterT`. (`Identity` is a trivial monad, it doesn't have any additional behavior.)

`MonadTrans` allows you to wrap underlying monad into the transformed one. You can live without it, but you will need to perform manual wrapping (see `MonadTrans` instance definition for `WriterT` for example how to do it). The only use case where you really need `MonadTrans` -- when you don't know actual type of transformer.

`MonadWriter` is a type class declared in `mtl`. It's methods (`writer`, `pass`, `tell` and `listen`) are the same as function for `WriterT`. It allows to wrap (automatically!) `WriterT` computation through stack of transformers, even if you don't know exact types (and even number!) of transformers in the stack.

So, `WriterT` is the only type which is "required".

For other monad transformers it is the same: `BaseT` is a transformer, `Base` is a monad without underlying monad and `MonadBase` is a type class -- class of all monads, that have `BaseT` somewhere in transformers stack.

ADDED:

You can find great explanation in RWH book

Here is a basic example:

import Control.Monad.Trans
import Control.Monad.Trans.Writer
import Control.Monad.Trans.Reader hiding (ask)

-- `ask` from transformers
-- ask :: Monad m => ReaderT r m r
import qualified Control.Monad.Trans.Reader as TransReader (ask)

-- `ask` from mtl
-- ask :: MonadReader r m => m r
import qualified Control.Monad.Reader as MtlReader (ask)

-- Our monad transformer stack:
-- It supports reading Int and writing String
type M m a = WriterT String (ReaderT Int m) a

-- Run our monad
runM :: Monad m => Int -> M m a -> m (a, String)
runM i action = runReaderT (runWriterT action) i

test :: Monad m => M m Int
test = do
  tell "hello"
  -- v <- TransReader.ask     -- (I) will not compile
  v1 <- lift TransReader.ask  -- (II) ok
  v2 <- MtlReader.ask         -- (III) ok
  return (v1 + v2)

main :: IO ()
main = runM 123 test >>= print

Note that `(I)` will be rejected by compiler (try it to see the error message!). But `(II)` compiles, thanks to `MonadTrans` ("explicit lifting"). Thanks to `MonadReader`, `(III)` works out of the box ("implicit lifting"). Please read RWH book for explanation how it works.

(In the example we import `ask` from two different modules, that is why we need qualified import. Usually you will use only one of them at a time.)

Also I didn't mean to specifically ask about `Writer`.

Not sure I understand... `Reader`, `State` and others use the same schema. Replace `Writer` with `State` and you will have an explanation for `State`.

Problem

I'm trying to learn monad transformers, based on the standard Haskell libraries (mtl? transformers? not sure which one came with my download of the Haskell platform - 7.4.1). What I believe I've noticed is a common structure for each monad transformer definition: base type ('Base') - Monad instance transformer type ('BaseT') Monad instance MonadTrans instance MonadIO instance transformer class ('MonadBase') some operations instances for other 'BaseT's So for example, for the Writer monad, there'd be: - a Writer datatype/newtype/type, with a Monad instance - a WriterT datatype/newtype/type, with Monad, MonadTrans, and MonadIO instances - a MonadWriter class, and instances of this class for StateT, ReaderT, IdentityT, ... Is this how monad transformers are organized? Am I missing anything/do I have any incorrect details? The motivation for this question is figuring out: - what the relationships and differences are between the "BaseT"s and the corresponding "MonadBase"s and "Base"s - whether all three are required - how MonadTrans is related and what its purpose is

Original source