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.