Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"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.

like image 818
theonlygusti Avatar asked Feb 07 '17 16:02

theonlygusti


2 Answers

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
like image 96
chepner Avatar answered Nov 15 '22 12:11

chepner


You need this strange operator (.).(.), which is sometimes defines as .: (think of 3 dots ...)

In ghci

Prelude> let (.:) = (.).(.)
Prelude> let f = (+) .: (+) 
Prelude> f 1 2 3 
> 6

Note this operator can also be defined as <$$> = fmap . fmap.

like image 26
mb14 Avatar answered Nov 15 '22 12:11

mb14