When learning the Reader Monad, I find that it is defined as:
newtype Reader r a = Reader { runReader :: r -> a }
instance Monad (Reader r) where
return a = Reader $ \_ -> a
m >>= k = Reader $ \r -> runReader (k (runReader m r)) r
I want to known why using function as constructor parameter instead of something else such as a tuple:
newtype Reader r a = Reader { runReader :: (r, a) }
instance Monad (Reader r) where
-- Here I cannot get r when defining return function,
-- so does that's the reason that must using a function whose input is an "r"?
return a = Reader (r_unknown, a)
m >>= k = Reader (fst $ runReader m) (f (snd $ runReader m))
According to the Reader definition, we need a "environment" which we can use to generate a "value". I think a Reader type should contain the information of "environment" and "value", so the tuple seems perfect.
You didn't mention it in the question, but I guess you thought specifically of using a pair for defining
Readerbecause it also makes sense to think of that as a way of providing a fixed environment. Let's say we have an earlier result in theReadermonad:We can use this result to do further calculations with the fixed environment (and the
Monadmethods guarantee it remains fixed throughout the chain of(>>=)):(If you substitute the definitions of
return,(>>=)andrunReaderin the expression above and simplify it, you will see exactly how it reduces to2 + 3.)Now, let's follow your suggestion and define:
If we have an environment of type
rand a previous result of typea, we can make anEnv r aout of them...... and we can also get a new result from that:
The question, then, is whether we can capture this pattern through the
Monadinterface. The answer is no. While there is aMonadinstance for pairs, it does something quite different:The
Monoidconstraint is needed so that we can usemempty(which solves the problem that you noticed of having to create ar_unknownout of nowhere) andmappend(which makes it possible to combine the first elements of the pair in a way that doesn't violate the monad laws). ThisMonadinstance, however, does something very different than what theReaderone does. The first element of the pair isn't fixed (it is subject to change, as wemappendother generated values to it) and we don't use it to compute the second element of the pair (in the definition above,ydoes not depend neither onrnor ons).Writeris a logger; thervalues here are output, not input.There is one way, however, in which your intuition is justified: we can't make a reader-like monad using a pair, but we can make a reader-like comonad. To put it very loosely,
Comonadis what you get when you turn theMonadinterface upside down:We can give the
Envwe had abandoned aComonadinstance:That allows us to write the
2 + 3example from the beginning in terms of(=>>):One way to see why this works is noting that an
a -> Reader r bfunction (i.e. what you give toReader's(>>=)) is essentially the same thing that anEnv r a -> bone (i.e. what you give toEnv's(=>>)):As further evidence of that, here is a function that changes one into the other:
To wrap things up, here is a slightly longer example, with
ReaderandEnvversions side-by-side: