Introduction

Some very brief notes summarizing Haskell’s reader monad. It’s my crib sheet, written partly to straighten matters in my own mind and partly for future reference.

Most of the information here comes from the usual places, notably the Typeclassopedia.1 I’m also indebted to Dominic Prior for many helpful discussions. Dominic is collecting useful and interesting monad examples2 on Google Docs.

The Reader monad

In Haskell the Reader Monad3 is the standard way to handle functions which need to read some sort of immutable environment.

As we’ve discussed before, the basic idea is to use the function monad4 ((->) e) wrapped in a type to make things more readable. However, if you look at the source the reality seems more complicated.

For me there are three distinct areas of confusion:

However, given that we have the source, we can easily make our own standalone version:

import Control.Monad							
import Control.Applicative						
									
data R e a = R (e -> a)							
									
instance Functor (R e) where						
  fmap f (R x) = R $ \e -> (f . x) e     				
									
instance Applicative (R e) where					
  pure x          = R $ \e -> x						
  (R f) <*> (R x) = R $ \e -> (f e) (x e)				
									
instance Monad (R e) where						
 return x = R $ \e -> x							
 x >>= f  = R $ \e -> runR (f (runR x e)) e				
									
runR :: R e a -> e -> a							
runR (R f) e = f e							
									
ask :: R a a								
ask = R $ \e -> e							

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!

In many ways runR does the opposite of R:

*Main> :t R				
R :: (e -> a) -> R e a			
					
*Main> :t runR				
runR :: R e a -> e -> a			
					
*Main> :t runR . R			
runR . R :: (e -> a) -> e -> a		
					
*Main> (runR . R) (+1) 1234		
1235					

So

runR . R = ($)

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:

instance Monad ((->) e) where
    return x = \e -> x
    x >>= f  = \e -> f (x e) e

instance Monad (R e) where						
    return x = R $ \e -> x						
    x >>= f  = R $ \e -> runR (f (runR x e)) e				

A simple return Reader doesn’t depend on the environment at all:

*Main> runR (return "Banana") 1234
"Banana"

To see why consider:

runR (return x) e = runR (R $ const x) e
                  = (runR . R) (const x) e
                  = (const x) e
                  = x

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:

*Main> :t R
R    :: (e -> a) -> R e a

*Main> :t asks
asks :: (e -> a) -> R e a

I’m still slightly unsure whether this is necessarily true for all MonadReaders though. Caveat lector!

local

local transforms the environment a Reader sees:

local :: (e -> t) -> R t a -> R e a	
local f r = do				
              e <- ask			
              return $ runR r (f e)

Again I prefer the desugared version:

local f r = fmap (\e -> runR r (f e)) ask

In the example below we’ll use ask as the Reader, which will just show us the environment:

*Main> runR ask "Chocolate"
"Chocolate"					

*Main> runR (local (++ " sauce") ask) "Chocolate"
"Chocolate sauce"				

Examples

Here’s a simple, contrived example:

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

f takes one explicit parameter and uses ask to read the environment. It returns both in a tuple.

To run the function, just use runR:

*Main> runR (f 10) 20
(10,20)

There are more sensible examples in the Control.Monad.Reader6 documentation.

R golf

One can play the usual hunt for cute ways to compose things.

If we desugar our toy f above it becomes,

f x = ask >>= \e -> return $ (x,e)
    = ask >>= return . (\e -> (x,e))
    = fmap (\e -> (x,e)) ask

or,

f x = asks (\e -> (x,e))

Given this:

*Main> runR (return 10 >>= f) 20  	
(10,20)					

*Main> runR ((f >=> f) 10) 20		
((10,20),20)				

*Main> runR (ask >>= f) 20		
(20,20)					

Or perhaps a spot of lifting:

*Main> runR (liftM (*10) ask) 20
200

*Main> runR (liftM2 (,) ask ask) 20
(20,20)

*Main> runR (liftM2 (,) (f 100)  ask) 20
((100,20),20)

Finally we could get Applicative:

*Main> runR ((,) <$> (f 10) <*> (f 100)) 20
((10,20),(100,20))