The Reader monad is used to pass one value as an argument to a number of function calls. This can be useful when you require some configuration or environment information accessible from a block of functions.
This monad is provided in Haskell’s standard libraries, but let’s have a go at creating it ourselves.
A sample problem
Say we have a
Person data type with a
address, and we want to write a
showPerson function to display these details in a string.
Notice how we need to pass the
person argument around everywhere? It would be nice to be able to have all this run in the context of a particular
person, and that’s what the Reader monad lets us do.
Rather than having the
z parts of the
showPerson function each evaluating to a string, how about we instead treat them as functions of type
Person -> String. We’d like to compose all these together and return a value which is a combination of the previous results, which sounds like something we can use a monad for.
Implementing a monad for single argument functions
Rather than the specific
Person -> String functions from the example, we can look at the general case of a function
r -> a, or a function that reads some value of type
r and returns some other value. We are interested in the
r -> bit, but Haskell won’t let us do that. Instead we need to write it as
1 + 2to prefix position as
(+) 1 2. This is just what we’ve done in moving
r -> ato
(->) r a.
All monads are functors, so let’s implement that first:
We can follow the same trail of types to implement the monad type class:
We can now rewrite our
showPerson example by binding together the functions that read the
Person information using
Alternatively we can use Haskell’s do-notation. (The two forms are equivalent; Haskell will turn this into the same code as the above snippet, but do-notation tends to look neater.)
We can import Control.Monad.Reader to make our
showPerson examples compile without having to define the reader implementation ourselves.
This implementation also provides a
Reader type and
ReaderT monad transformer, along with
local functions to access the passed in environment, and a
runReader function to pass an environment to a
Reader and get the result. I haven’t had a need for these yet, but thought I’d mention them as they crop up in examples all the time. (I’m guessing these functions are most useful when using reader with monad transformers, but I’m still trying to work those out. :))