Reputation: 839
I am trying to make sense of the reader monad but can't seem to grasp what bind (>>=) does in this monad.
Here's the implementation I am analyzing:
newtype Reader e a = Reader { runReader :: (e -> a) }
instance Monad (Reader e) where
return a = Reader $ \e -> a
(Reader r) >>= f = Reader $ \e -> runReader (f (r e)) e
(Reader r)
instead of (Reader r a)
.(f (r e))
, what is it's purpose?Thanks a lot for helping me out.
Upvotes: 2
Views: 336
Reputation: 43852
My first question is, why is Reader partially applied on the left hand side of bind?
(Reader r)
instead of(Reader r a)
.
It isn’t. That use of Reader
is fully saturated, as it must be. However, I can understand your confusion… remember that in Haskell, types and values are in different namespaces, and defining a datatype with data
or newtype
brings new names into scope in both namespaces. For example, consider the following declaration:
data Foo = Bar | Baz
This definition binds three names, Foo
, Bar
, and Baz
. However, the part on the left-hand side of the equals sign is bound in the type namespace, since Foo
is a type, and the constructors on the right hand side are bound in the value namespace, since Bar
and Baz
are essentially values.
All of these things have types as well, which can be helpful to visualize. Foo
has a kind, which is essentially the “type of a type-level thing”, and Bar
and Baz
both have a type. These types can be written as follows:
Foo :: *
Bar :: Foo
Baz :: Foo
…where *
is the kind of types.
Now, consider a slightly more complicated definition:
data Foo a = Bar Integer String | Baz a
Once again, this definition binds three names: Foo
, Bar
, and Baz
. Once again, Foo
is in the type namespace and Bar
and Baz
are in the value namespace. Their types, however, are more elaborate:
Foo :: * -> *
Bar :: Integer -> String -> Foo a
Baz :: a -> Foo a
Here, Foo
is a type constructor, so it’s essentially a type-level function that accepts a type (*
) as an argument. Meanwhile, Bar
and Baz
are value-level functions that accept various values as arguments.
Now, return to the definition of Reader
. Avoiding record syntax for a moment, we can reformulate it as follows:
newtype Reader r a = Reader (r -> a)
This binds one name in the type namespace and one name in the value namespace, but the confusing part is that they’re both named Reader
! This is totally allowed in Haskell, though, since the namespaces are separate. Each Reader
has a kind/type in this case, too:
Reader :: * -> * -> *
Reader :: (r -> a) -> Reader r a
Note that the type-level Reader
takes two arguments, but the value-level Reader
only takes one. When you pattern-match against a value, you are using the value-level constructor (since you’re deconstructing a value that was built up using that same constructor), and the value only contains one value (as it must, since Reader
is a newtype
), so the pattern only binds a single variable.
What goes on in this part of the definition:
(f (r e))
, what is it's purpose?
Reader
is essentially a mechanism for composing a lot of functions that all take the same argument. It’s a way to avoid having to thread a value around everywhere, since the various instances will do the plumbing automatically.
To understand the definition of >>=
for Reader
, let’s specialize the type of >>=
to Reader
:
(>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b
For the purpose of clarity, we can also expand Reader r a
to r -> a
, just to get a better intuition for what the type actually means:
(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)
Let’s also name the arguments here for the purpose of discussion:
(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)
(>>=) f g = ...
Let’s think about this for a moment. We are given two functions, f
and g
, and we are expected to produce a function that produces a value of type b
from a value of type a
. We only have one way to produce a b
, and that’s by calling g
. But in order to call g
, we have to have an a
, and we only have one way to get an a
: call f
! We can call f
, since it only needs an r
, which we already have, so we can start attaching the functions together to produce the b
we need.
This is a little confusing, so it might help to see this flow of values visually:
+------------+
| input :: r |
+------------+
| |
v |
+--------------+ |
| f input :: a | |
+--------------+ |
| |
v v
+------------------------+
| g (f input) input :: b |
+------------------------+
In Haskell, this looks like this:
f >>= g = \input -> g (f input) input
…or, renaming things a little to match the definition in your question:
r >>= f = \e -> f (r e) e
Now, we need to reintroduce some wrapping and unwrapping, since the real definition is on the Reader
type, not (->)
directly. This means we need to add some uses of the Reader
wrapper and the runReader
unwrapper, but otherwise the definition is the same:
Reader r >>= f = Reader (\e -> runReader (f (r e)) e)
At this point, you can check your intuition: Reader
is a way to thread a value around between a lot of functions, and here we’re composing two functions, r
and f
. Therefore, we should need to pass the value in twice, which we do: there are two uses of e
in the above definition.
Upvotes: 10
Reputation: 233150
There's no partial application. Reader
is a newtype
definition that 'wraps' exactly one value (runReader
), which is a function of the type e -> a
. So, Reader r
just pattern-matches the function out of the Reader
wrapper. r
is bound to the function of type e -> a
.
f
is a function, as is r
. r e
invokes the function r
with the value e
, and then f
is invoked with the result of that function call.
Upvotes: 1
Reputation: 12123
To answer your function, Monad
is class over types of kind * -> *
.
[Int] :: *
= list of integers[] :: * -> *
= listReader e a :: *
= reader with environment e resulting in aReader e :: * -> *
= reader with environment eReader :: * -> * -> *
= readerWe are lousy when we say Reader
has a monad instance, when we mean
Reader
with any environment has a monad instance.
(Similarly, Writer w
is a Monad
when w
is a Monoid
, Writer
isn't a
Monad
).
To answer your second question, it's easier to think in terms of
the Reader e a
as the same as function e -> a
.
Function has the same monad definition, but without newtype
wrappers and
unwrappers. Think Reader = (->)
:
instance Monad ((->) e) where
return x = \_ -> x -- alternatively, return = const !
r >>= f = \e -> f (r e) e
Yet, the join
definition is probably most insightful:
join :: Reader e (Reader e a) -> Reader e a
But with bare function:
join :: (e -> e -> a) -> (e -> a)
join f e = f e e
And if we write it for Reader
we have to add runReader
and Reader
in
right places (and move variable binding to the lambda on RHS):
join f = Reader $ \e -> runReader (runReader f e) e
Upvotes: 4