As dave4420 mentions,
(.) :: (b -> c) -> (a -> b) -> a -> c
So what is the type (.) (.) ? dave4420 skips this part, so here: (.) takes a value of type b -> c as its first argument, therefore
(.) :: ( b -> c ) -> (a -> b) -> a -> c (.) :: (d -> e) -> ((f -> d) -> f -> e)
therefore, we have b ~ d->e and c ~ (f -> d) -> f -> e , and the resulting type (.)(.) is (a -> b) -> a -> c . Substituting, we get
(a -> d -> e) -> a -> (f -> d) -> f -> e
Renaming, we get (a -> b -> c) -> a -> (d -> b) -> d -> c . This is a function f that expects a binary function g , a value x , a unary function h and another value y :
fgxhy = gx (hy)
This is the only way to implement this type: gx :: b -> c , hy :: b and so gx (hy) :: c , if necessary.
Of course, in Haskell, a βunaryβ function is such that it expects one or more arguments; likewise, a binary function is such that it expects two or more arguments. But at least two (therefore, the use of, for example, succ out of the question).
We can also solve this by writing equations, combinators -style 1 . Rational reasoning is easy :
(.) (.) xyzwq = ((.) . x) yzwq = (.) (xy) zwq = (xy . z) wq = xy (zw) q
We simply add as many variables as needed to the mix, and then apply the definition back and forth. q was superfluous here, so we can throw it away and get the final definition,
_BB xyzw = xy (zw)
(coincidentally, (.) is known as B -combinator ).
1 abc = (\x -> ... body ...) equivalent to abcx = ... body ... , and vice versa, provided that x does not appear among {a,b,c} .