For a given type r
, the function of type r -> a
can be thought of as a computation delivering an a
using an environment typed r
. Given two functions r -> a
and a -> (r -> b)
, it's easy to imagine that one can compose these when given an environment (again, of type r
).
But wait! That's exactly what monads are about!
So we can create an instance of Monad for (->) r
that implements f >>= g
by passing the r
to both f
and g
. This is what the Monad instance for (->) r
does.
To actually access the environment, you can use id :: r -> r
, which you can now think of as a computation running in an environment r
and delivering an r
. To create local sub-environments, you can use the following:
inLocalEnvironment :: (r -> r) -> (r -> a) -> (r -> a)
inLocalEnvironment xform f = env -> f (xform env)
This pattern of having an environment passed to computations that can then query it and modify it locally is useful for not just the (->) r
monad, which is why it is abstracted into the MonadReader
class, using much more sensible names than what I've used here:
http://hackage.haskell.org/packages/archive/mtl/2.0.1.0/doc/html/Control-Monad-Reader-Class.html
Basically, it has two instances: (->) r
that we've seen here, and ReaderT r m
, which is just a newtype
wrapper around r -> m a
, so it's the same thing as the (->) r
monad I've described here, except it delivers computations in some other, transformed monad.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…