First write _B fgx = f (gx) = (f . g) x .
Since f $ x = fx , then (.)$(.) = _B $ _B = _B _B . Its type is obtained mechanically, since
0. (.) :: ( b -> c ) -> ((a -> b) -> (a -> c)) 1. (.) :: (b1 -> c1) -> ((a1 -> b1) -> (a1 -> c1)) 2. (.) (.) :: {b ~ b1 -> c1, c ~ (a1 -> b1) -> (a1 -> c1)} (a -> b) -> (a -> c) :: (a -> b1 -> c1) -> a -> (a1 -> b1) -> (a1 -> c1) :: (a -> b -> c ) -> a -> (a1 -> b ) -> a1 -> c
a and a1 are two different type variables, like b and b1 . But since there is no b or c in the final type, we can rename b1 and c1 back to only b and c to simplify. But not a1 .
We can read this type, in fact: it gets the f :: a -> b -> c binary function; x :: a value of the argument, g :: a1 -> b unary function and another value is y :: a1 and combines them in the only possible way that the types match:
fx :: b -> c gy :: b fx (gy) :: c
The rest has already been answered. Abbreviations are usually easier to perform in combinatorial equations, such as _B _B fxgy = _B (fx) gy = fx (gy) , with only two applications of the definition of _B (we can always add as many arguments as we need).
Will ness
source share