I am having difficulty understanding how a function can be a monad.
Function (->) r
is a monad according to a declaration in Control.Monad.Instances
:
instance Monad ((->) r) where
return x = \_ -> x
h >>= f = \w -> f (h w) w
Even what Miran Lipovača says about it makes me confused:
The implementation for
>>=
seems a bit cryptic, but it's really not all that. When we use>>=
to feed a monadic value to a function, the result is always a monadic value. So in this case, when we feed a function to another function, the result is a function as well. That's why the result starts off as a lambda. All of the implementations of>>=
so far always somehow isolated the result from the monadic value and then applied the function f to that result. The same thing happens here. To get the result from a function, we have to apply it to something, which is why we do(h w)
here to get the result from the function and then we apply f to that. f returns a monadic value, which is a function in our case, so we apply it to w as well.
The type signature of (>>=) is this: (>>=) :: m a -> (a -> m b) -> m b
So I take that h
is typed as m a
and f
as (a -> m b)
. If a function is m a
, does it return an a
type value? or does it return something else taking an a
type?
If the non-monad value of h
is fed to f
, then we get:
f (h w)
Looks fine. Since f
is a function and has taken its sole argument, it is already a value, no? Since it's a monadic function the value is also a monadic value. Why then does it need another value w
? Doesn't feeding w
to f something
make it non-monadic, i.e., it is not a function any more, no? I also cannot understand why f something
and h
take the same argument w
and return different value types (m a
and m b
).
First, here's the type of (>>=)
:
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
Now, with m
specialized to ((->) r)
:
(>>=) :: ((->) r) a -> (a -> ((->) r) b) -> ((->) r) b
Rewritten with all the function arrows infix:
(>>=) :: (r -> a) -> (a -> (r -> b)) -> (r -> b)
Removing some superfluous parentheses:
(>>=) :: (r -> a) -> (a -> r -> b) -> r -> b
At which point it should be much easier to see what's going on: the third argument (of type r
) is given to the first argument to get something of type a
, then both that result and the third argument are given to the second argument to get a final result of type b
.
So, ((->) r)
as a Monad
represents an extra function argument to every value in that monad, and when monadic values are combined the single "extra" argument is duplicated and given to each input value. Essentially, this creates a "read-only global environment" for monadic values. This interpretation is provided explicitly as the Reader
monad, which is just a wrapper around ((->) r)
.
It's perhaps easier to understand this monad by looking at what join
does, since a monad can equivalently be defined using fmap
and join
instead of >>=
.
The general form of join
has the type Monad m => m (m b) -> m b
, so it takes a "two-layer" monadic value and crunches it down to one layer.
With the function monad, m ~ (a ->)
, so join
has the type (a -> a -> b) -> (a -> b)
so it takes a function of two arguments and returns a function that only takes one.
join :: (a -> a -> b) -> (a -> b)
join f = \x -> f x x
As you can see, it just duplicates the argument.
Similarly, fmap
on functions is just function composition, and return
is const
.
I think it's much easier to understand this way than trying to make sense of >>=
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With