Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function as an Instance of Monad

Tags:

haskell

monads

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).

like image 991
amemus Avatar asked Oct 26 '12 02:10

amemus


2 Answers

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).

like image 66
C. A. McCann Avatar answered Sep 21 '22 14:09

C. A. McCann


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 >>=.

like image 32
hammar Avatar answered Sep 18 '22 14:09

hammar