Search code examples
functionhaskellcomposition

"chain" the addition operator into a function that accepts 3 arguments?


I'd like to compose the addition operator (+) to make a function of this type:

Num a => a -> a -> a -> a

Like, the equivalent of this:

(\a b c -> a + b + c)

but without having to resort to lambdas.


I tried already

((+) . (+))

which I would have expected to work but surprisingly didn't.


Solution

  • http://pointfree.io gives the point-free version of \a b c -> a + b + c as ((+) .) . (+).

    Informally, composition only works "intuitively" for first-order functions, which neither take functions as arguments nor return functions as values. (+) is a 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 fashion, the result is not what you expect:

    :t (+) . (+)
    (+) . (+) :: (Num a, Num (a -> a)) => a -> (a -> a) -> a -> a
    

    Consider the definitions of the two functions:

    (+) :: Num z => z -> z -> z
    (.) :: (b -> c) -> (a -> b) -> (a -> c)
    f . g = \x -> f (g x)
    

    Then

    (+) . (+) == (.) (+) (+)
              == \x -> (+) ((+) x)
    

    Because of currying, you wind up passing a function, not a number, as the first argument of the first (+).


    So how do we get from h a b c = a + b + c to h = ((+) .) . (+)? Start by rewriting the infix expression as a prefix expression, using the fact that (+) is left-associative.

    \a b c -> a + b + c
         == \a b c -> ((+) a b ) + c
         == \a b c -> (+) ((+) a b) c
    

    Next, we alternately apply eta conversion to eliminate an argument and composition to move an argument into position to be eliminated. I've tried to be very explicit about identifying the functions use for the application of composition.

         == \a b -> (+) ((+) a b)      -- eta conversion to eliminate c
         == \a b -> (+) (((+) a) b)    -- parentheses justified by currying
         --          f      g          -- f = (+), g = ((+) a)
         -- \a b ->  f  (   g    b)
         -- \a b -> (f   .  g)   b     -- definition of (.)
         == \a b -> ((+) . ((+) 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 (.)
         --           f       g        -- f = ((+) .), g = (+)
         -- \a ->     f     (g a)
         -- \a -> (   f   .   g) a     -- definition of (.)
         == \a -> (((+) .) . (+)) a
         == ((+) .) . (+)              -- eta conversion to eliminate a