"chaining" of the addition operator to a function that takes 3 arguments?

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

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

Like, the equivalent of this:

 (\abc -> a + b + c) 

but without the need to resort to lambdas.


I have already tried

 ((+) . (+)) 

which I would expect to work, but surprisingly was not.

+6
source share
4 answers

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 
+11
source

You need this strange operator (.).(.) , Which is sometimes defined as .: (Think of three points ...)

In ghci

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

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

+8
source

Although this makes some noise, you can use uncurry :: (a -> b -> c) -> (a,b) -> c and curry :: ((a,b) -> c) -> a -> b -> c to temporarily store the arguments of the second plus in one tuple:

 curry $ (+) . uncurry (+) :: Num a => a -> a -> a -> a 

or perhaps more semantically readable:

 curry ((+) . uncurry (+)) :: Num a => a -> a -> a -> a 

uncurry thus executes a function (here (+) ) and converts it into a function: uncurry (+) :: Num a => (a,a) -> a . So you converted (+) to a function that takes a tuple .

Now we can use (.) To create a composition with the first (+) :

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

So now we have a function that takes one argument (tuple (a,a) ) and creates a function that takes a (second operand of the first (+) ) and calculates the sum. The problem, of course, is that we want to get rid of the tuple. We can do this by passing a function to curry . This converts the tuple function ( (a,a) -> (a -> a) ) into a function that takes arguments separately ( a -> (a -> (a -> a)) ).

+6
source

Pay attention to the signature of the function composition operator:

 (.) :: (b -> c) -> (a -> b) -> a -> c ^ ^ ^ Functions 

It takes 2 functions, each of which takes 1 argument and returns a function that takes an argument of the same type as the second, and returns the same type as the first.

Your attempt to make two + did not work, as + takes 2 arguments, so without any hacker / creative workaround this is not possible.

At this point, I would say that forcing a composition when it does not correspond to this problem will simply complicate your life.

If you want to summarize several numbers, you can write a function such as:

 sum :: [Int] -> Int sum nums = foldl (+) 0 nums 

Or, since nums appears at the end of a definition, it can be discarded altogether by getting a dot-free form:

 sum :: [Int] -> Int sum = foldl (+) 0 

It reduces / adds + over a list of numbers. If you have not used the folds, check them now . They are one of the main ways to achieve a loop in Haskell. This is essentially “implicit recursion” when you are dealing with lists or something else iterative.

Using the above function, you can use it as:

 sum [1, 2 3, 4, 5] 
+4
source

Source: https://habr.com/ru/post/1014771/


All Articles