http://pointfree.io gives a meaningless version of \abc -> a + b + c
as ((+) .) . (+)
((+) .) . (+)
.
Unofficially, composition only works “intuitively” for first-order functions that do not accept functions as arguments and do not return functions as values. (+)
- higher order function; it takes a value of type Num a => a
and returns a function of type Num a => a -> a
. When you try to compose higher-order functions in a naive way, the result is not what you expect:
:t (+) . (+) (+) . (+) :: (Num a, Num (a -> a)) => a -> (a -> a) -> a -> a
Consider the definitions of two functions:
(+) :: Num z => z -> z -> z (.) :: (b -> c) -> (a -> b) -> (a -> c) f . g = \x -> f (gx)
Then
(+) . (+) == (.) (+) (+) == \x -> (+) ((+) x)
Because of currying, you end up passing the function, not the number, like the first argument of the first (+)
.
So, how do we get from habc = a + b + c
to h = ((+) .) . (+)
h = ((+) .) . (+)
? Start by rewriting the infix expression as a prefix expression, using the fact that (+)
left associative.
\abc -> a + b + c == \abc -> ((+) ab ) + c == \abc -> (+) ((+) ab) c
Then we alternately apply the eta transform to exclude the argument and composition in order to move the argument to a position that needs to be eliminated. I tried to very clearly define the functions used to apply the composition.
== \ab -> (+) ((+) ab) -- eta conversion to eliminate c == \ab -> (+) (((+) a) b) -- parentheses justified by currying -- fg -- f = (+), g = ((+) a) -- \ab -> f ( gb) -- \ab -> (f . g) b -- definition of (.) == \ab -> ((+) . ((+) a)) b == \a -> (+) . ((+) a) -- eta conversion to eliminate b == \a -> (.) (+) ((+) a) -- prefix notation == \a -> ((.) (+)) ((+) a) -- parentheses justified by currying == \a -> ((+) . )((+) a) -- back to a section of (.) -- fg -- f = ((+) .), g = (+) -- \a -> f (ga) -- \a -> ( f . g) a -- definition of (.) == \a -> (((+) .) . (+)) a == ((+) .) . (+) -- eta conversion to eliminate a
source share