
Paul Sargent
Digging a little deeper, I decided to look at the definition of ap:
ap = liftM2 id
liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r liftM2 f m1 m2 = do { x1 <- m1; x2 <- m2; return (f x1 x2) }
liftM2 makes sense to me, but again we seem to have a mismatch of types. liftM2 wants a two argument function for it's first argument, but ap provides id. I'm finding myself tumbling further and further down the rabbit hole.
id has type a -> a, so if you "instantiate" a to `a2 -> r', you get: id :: (a2 -> r) -> (a2 -> r) Since -> is right-associative this is the same as: id :: (a2 -> r) -> a2 -> r So that's how id can be seen as a two-argument function in this case. That results in liftM2 id having the type: liftM2 id :: (Monad m) => m (a2 -> r) -> m a2 -> m r If you now insert the Reader monad for m, you get: liftM2 id :: (s -> a2 -> r) -> (s -> a2) -> (s -> r) So appying (liftM2 id), which is ap, to (+), causes s, a2 and r to be the same type, since (+) has type (Num a) => a -> a -> a. liftM2 id (+) :: Num a => (a -> a) -> a -> a If you then apply that to id, as in ap (+) id, you get: ap (+) id = liftM2 id (+) id :: Num a => a -> a So this explains the type, and if you then expand the definitions of liftM2 and >>= and return for the Reader monad, you will see that this corresponds to \x -> x + x. Regards, Daniel