Skip to content

WriterT

By now the pattern should be clear:

Maybe a is a value of type Maybe a, so MaybeT wraps an effectful computation that returns Maybe a:

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

Reader r a wraps a function of type r -> a, so ReaderT wraps a function of type r -> m a:

newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }

Now let's try this for Writer. Writer w a wraps a value of type (a, w), so WriterT wraps an effectful computation that returns a pair of type (a, w):

newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }

Just as for MaybeT and ReaderT, the Monad instance for WriterT implements the same logic as the one we developed for Writer before, only we need to do so using functions in the monad m instead of pure functions:

instance (Monad m, Monoid w) => Monad (WriterT w m) where
    return x = WriterT $ return (x, mempty)
    x >>= f  = WriterT $ do
        (y, wx) <- runWriterT x
        (z, wf) <- runWriterT (f y)
        return (z, wx <> wf)

Writer is once again implemented by applying WriterT to Identity as the underlying monad:

type Writer w = WriterT r m

runWriter = runIdentity . runWriterT

Our function tell, which we implemented like this before

tell :: w -> Writer w ()
tell w = Writer ((), w)

now becomes

tell :: Monad m => w -> WriterT w m ()
tell w = WriterT $ return ((), w)

I won't give an example of using WriterT here. If you remember that Writer or WriterT is your weapon of choice if you need to build a log of some form, that's enough. I tend to use Writer and WriterT rarely, probably because I don't write a lot of business applications that could benefit from such a logging facility.