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:
- The Reader monad is defined as a monad transformer5, namely
ReaderT Identity
. - There are many ways to implement a Reader Monad, and so we work in terms of a MonadReader class.
- The Reader constructor is defined using record syntax, which I always find confusing in non-trivial cases. I think this is a rather subjective failing though.
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))
References
- 1. http://www.haskell.org/haskellwiki/Typeclassopedia
- 2. https://docs.google.com/document/d/1DvbcQTibeUEOVmoLO14vvRa27kf6y29sObUmQpyFn9g/pub
- 3. http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Reader.html#1
- 4. ../07/monads-fn.html
- 5. http://en.wikipedia.org/wiki/Monad_transformer
- 6. http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Reader.html#1