How do I derive the type for this function:
It helps to do two things before everything else:
- put explicit parentheses so that
x->y->z
becomesx->(y->z)
- rename type variables so that there are no clashes
Wit this in mind let's rewrite the types
(=<<) :: Monad m => (a -> m b) -> (m a -> m b)
zip :: [x] -> ([y] -> [(x, y)])
Now match the types. The first argument to =<<
is zip
, so (a -> m b)
is the same as [x] -> ([y] -> [(x, y)])
.
a -> m b
[x] -> ([y] -> [(x, y)])
So a
is [x]
and m b
is ([y] -> [(x, y)])
. Rewriting ->
in prefix notation, we get -> [y] [(x, y)]
, which is the same as (-> [y]) [(x, y)]
.
m b
(-> [y]) [(x, y)]
So m
is (-> [y])
(which is a monad indeed) and b
is [(x, y)]
.
So now we know what is a
, what is b
and what is m
. Let's rewrite (m a -> m b)
in these terms:
(m a) -> (m b)
((-> [y]) [x]) -> ((-> [y]) [(x, y)])
Rewriting in the infix style again, we get
([y] -> [x]) -> ([y] -> [(x, y)])
which is, up to variable names, is the same answer GHC is giving you.
(zip =<<)
is equivalent to (>>= zip)
, which makes it perhaps a bit more readable. Either way, zip
occupies the (a -> m b)
slot in those functions, as you've correctly observed.
One more intuitive transformation to make is thinking about the arity of =<<
. It "takes" two parameters, so if we apply it to one, we should only be left with one more. Hence, the signature ([b] -> [a]) -> [b] -> [(a, b)]
is an unary function!
(zip =<<) :: ([b] -> [a]) -> ([b] -> [(a, b)])
------------ -----------------
m a' m b'
So what's m
? The Monad instance exists for functions (r ->)
(or, alternatively, (->) r
). So in this case r :: [b]
(and thus m :: ([b] ->)
), a' :: [a]
and b' :: [(a, b)]
.
Consequently, zip
fits just as we asserted at the beginning:
a' -> m b' -- substitute [(a,b)] for b'
a' -> m [(a, b)] -- substitute [a] for a'
[a] -> m [(a, b)] -- substitute ([b] ->) for m
[a] -> ([b] -> [(a,b)])
[a] -> [b] -> [(a,b)]