Thank you Willem Van Onsem for a great answer.
Understand the zipWith id from the eyes of the ghc type output system.
first consider the zipWith type
Prelude> :info zipWith zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] -- Defined in 'GHC.List'
The first argument to zipWith is a function that takes a function that takes two arguments.
(a -> b -> c) can also be rewritten as a -> (b -> c)
Now consider zipWith id . id type from a -> a
we put id in the place where the function of the two arguments should go.
So a type expression would make (a -> b -> c) look like a -> (b -> c) (notification a -> (b -> c) takes one arument a and gives b -> c ie one function argument.)
But by making a -> (b -> c) , an identity function would be possible only if a is (b → c).
When a is (b → c), the function a -> b -> c becomes ((b → c) → (b → c))
Thus, the type input system displays a as (b -> c) , and the resulting result will [a] -> [b] -> [c] replace a with b -> c .
Replace a with (b → c).
Make (a → b → c) look like id . (a → b → c) can be made similar to id specified replacement.
((b → c) → b → c), which can also be written as ((b → c) → (b → c)), which id :: x -> x where x b → c)
zipWith :: ((b -> c) -> b -> c) -> [b -> c] -> [b] -> [c]
So, we get the output as [b -> c] -> [b] -> [c]