Here we’ve called the monad R rather than Reader to avoid conflicts with the real thing.

Notice that the functions we must implement to make the functor, applicative and monad instances all begin R $ \e -> ... on the right-hand side. This means that when we’re thinking about such values we can write them as R x without loss of generality.

The definitions above are written so as to emphasize the R $ \e -> ... part. You might prefer to say:

return = R . const
ask = R id

I think the key intuition is that Readers are functions from a particular enviroment (wrapped in R).

Beside the core functions above, our new Reader also provides runR and ask. We’ll need these to use the monad in practice.

runR

There’s no standard way to extract a value from a monad, which means that for the Reader instance to be useful we will need a function to actually run the Reader in a given environment, and return the result. runR is that function!

ask

The other extra function, ask, provides a way to easily access the environment. Typically we’ll use it in a string of actions expressed in do-notation, but for now let’s try something simpler. ask has type R a a, so given an environment of type a we can run it:

*Main> runR ask 1234
1234

It’s straightforward to see this:

runR ask = runR (R $ \e -> e)
= runR (R id)
= (runR . R) id
= id

return and >>=

Being a monad we will need return and >>=. Happily these are just translations of the definitions for ((->) e) sprinkled with R and runR:

Subsidiary functions

Besides the functions above, we also define a couple more to make life easier. I think the functions above are a sufficient set, in the sense that you can define everything in terms of them, but I’m not sure that they’re the set actually used by the MonadReader class.

asks

ask lets us read the environment and then play with it. asks takes a complementary approach: given a function it returns a Reader which evaluates that function and returns the result.

asks :: (e -> a) -> R e a
asks f = do
e <- ask
return $ f e

Here’s an example:

*Main> runR (asks length) "Banana"
6

asks can be very elegantly implemented in terms of fmap:

asks f = fmap f ask

This simplicity hints at a deeper observation: asks is effectively the constructor R. Just look at the types: