In order for zipWith ($)
to typecheck we must unify the type of zipWith
's first argument with the type of ($)
. I'll write them out together and with unique names to make it more clear.
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
($) :: ((x -> y) -> x -> y)
Thus, zipWith
typechecks if and only if we can assume that a ~ (x -> y)
, b ~ x
and c ~ y
. There's nothing stopping this unification from succeeding, so we can substitute these names back into the type for zipWith
.
zipWith :: ((x -> y) -> x -> y) -> [x -> y] -> [x] -> [y]
($) :: ((x -> y) -> x -> y)
And then proceed with application since everything matches up nicely now
zipWith ($) :: [x -> y] -> [x] -> [y]
which is equivalent up to the specific choice of type variable names with the type you saw.