Instances for ReaderT, WriterT, and StateT
To see this all in action, let's define MonadReader
, MonadWriter
, and
MonadState
instances for ReaderT
, WriterT
, and StateT
:
instance Monad m => MonadReader r (ReaderT r m) where
ask = ReaderT return
local mod f = ReaderT $ runReaderT f . mod
reader f = ReaderT $ return . f
instance (Monoid w, Monad m) => MonadWriter w (WriterT w m) where
tell w = WriterT $ return ((), w)
listen f = WriterT $ do
(x, w) <- runWriterT f
return ((x, w), w)
pass f = WriterT $ do
((x, flt), w) <- runWriterT f
return (x, flt w)
writer f = WriterT $ return f
instance Monad m => MonadState s (StateT s m) where
get = StateT $ \s -> return (s, s)
put s = StateT $ \_ -> return ((), s)
state f = StateT $ return . f
The implementations of ask
, tell
, get
, and put
are exactly the ones from
our earlier discussions of the ReaderT
, WriterT
, and StateT
monad
transformers, only now they have become methods of the MonadReader
,
MonadWriter
, and MonadState
instances.
For our MonadReader r (ReaderT r m)
instance, local mod f
should run f
in
some context modified by mod
, that is, it should be defined as
local mod f = ReaderT $ \r -> runReaderT f (mod r)
We obtain the definition given above using simple rewriting:
ReaderT $ \r -> runReaderT f (mod r)
= ReaderT $ \r -> (runReaderT f . mod) r -- Definition of function composition
= ReaderT $ runReaderT f . mod -- \r -> (runReaderT f . mod) r
-- applies (runReaderT f . mod) to its
-- argument and thus is the same as
-- runReaderT f . mod
reader f
takes the function f
and composes it with return
to obtain a
function in the monad m
. By wrapping this in ReaderT
, this becomes an action
in the ReaderT r m
monad.
Similarly, the implementation of writer f
for WriterT
takes the given pair
f
composed of a return value and a log value and turns it into an action in
the underlying monad m
, using return f
, and then it turns this into a action
in the WriterT w m
monad by wrapping it in WriterT
.
The implementations of listen
and pass
are fairly straightforward.
listen f
runs f
and collects the return value x
and log w
produced by
f
. Since listen f
should produce the same log but also return it as part of
the return value of f
, we simply return the pair ((x, w), w)
. pass f
runs
f
to obtain the pair ((x, flt), w)
, where (x, flt)
is the return value
produced by f
, and w
is the log. pass f
should produce the log flt w
and
return x
, so we return the pair (x, flt w)
.
For the MonadState
instance of StateT s m
, we already observed that the
implementations of get
and put
are the same we discussed earlier when
introducing the StateT
monad transformer. So the only thing to discuss is
state
. Its definition is exactly the same as that of reader
for
ReaderT r m
. We take the pure function f
and turn it into the function in
the monad m
by composing it with return
. Wrapping this function in StateT
then produces an action in the StateT s m
monad.
Exercise
When discussing the Reader
and State
monads, we also introduced
functions
asks :: (r -> a) -> Reader r a
gets :: (s -> a) -> State s a
Instead of returning the context or current state, they return the result of
applying the given function to the context or current state. These functions
are not part of the MonadReader
and MonadState
type classes. Yet they
are still available for any monad m
that is an instance of MonadReader
or MonadState
:
asks :: MonadReader r m => (r -> a) -> m a
gets :: MonadState s m => (s -> a) -> m a
Implement these two functions. Since all you know about m
is that it is an
instance of MonadReader
or MonadState
, your implementations cannot use
any functions other than the ones provided by these type classes or by the
Monad
and Functor
type classes, because every MonadReader
or
MonadWriter
is also a Monad
and, hence, a Functor
.
Solution
fmap
to the rescue. That's all we need. The return type of ask
is
m r
, and m
is a functor. Thus, we transform the value returned by
m
by applying a pure function to it. This gives
asks :: MonadReader r m => (r -> a) -> m a
asks f = fmap f ask
or, in infix notation,
asks :: MonadReader r m => (r -> a) -> m a
asks f = f <$> ask
The implementation of gets
is completely analogous:
gets :: MonadState s m => (s -> a) -> m a
gets f = f <$> get
Exercise
When discussing the State
monad and the StateT
monad transformer, we
also tasked about the function modify
, which we can use to modify the
state of the monad using a pure function of type s -> s
. Here was the
definition we had for the State
monad:
modify :: (s -> s) -> State s ()
modify f = State $ \s -> ((), f s)
This function is not part of the MonadState
type class, but it is still
available for any monad that is an instance of this type class. Here's the
type signature:
modify :: MonadState s m => (s -> s) -> m ()
We have a monad m
with some state s
, and modify f
allows us to modify
this state by applying f
to
it. Implement
modify. Since all you know about
mis that it is an instance of
MonadState, your implementation cannot use any functions other than
get,
put, and
state`.
Solution
There are two solutions. The more obvious one (?) uses get
to access
the current state s
, and then writes the updated state f s
using
put
:
modify :: MonadState s m => (s -> s) -> m ()
modify = do
s <- get
put $ f s
or desugaring this using (>>=)
:
modify :: MonadState s m => (s -> s) -> m ()
modify = get >>= put . f
We can also implement modify
using state
. Since modify f
should
have type m ()
, if we want modify f = state g
, for some function
g
, then g
must have type s -> ((), s)
, and the second component of
this pair should be the result of updating the old state by applying
f
. Easy enough: g = \s -> ((), f s)
. Thus,
modify :: MonadState s m => (s -> s) -> m ()
modify f = state $ \s -> ((), f s)