Here's the haskell code:
zipWith compare `ap` tail
where
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
compare :: a -> a -> Ordering
ap :: Monad m => m (a -> b) -> m a -> m b
So from what I understand ap in this case will need a function wrapped inside the list monad, that is it needs m (a -> b), which is supposedly fulfilled by zipWith compare but I don't see how, because zipWith compare will take on type [a] -> [a] -> [Ordering] which is not quite correct. Is there something happening behind the scenes of the list monad that causes the necessary type conversion?
The code works and is not my own work, I'm just trying to understand how it's permissible.
The trick here is that
a ->is a Monad!In fact, we can list out the instances of Monad in a plain ghci session:
Therefore,
zipWith compare :: m ([a] -> Ordering)andtail :: m [a]wheremis((->) [a]), or([a] ->).The trick here is that the function arrow,
->, is indeed a type constructor that builds new types. Once getting around this, you can take a look at the actual implementation of its Monad instance (and it's nothing out of expectation there).In fact, for
ap(I haven't double-checked but I'm pretty confident), it looks like this:This is also the S-combinator in SKI.