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.