Skip to content

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. Implementmodify. Since all you know aboutmis that it is an instance ofMonadState, your implementation cannot use any functions other thanget,put, andstate`.

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)