What is the shortest way we can express the function

f(a,b)(c,d)=(a+c,b+d)


in point-free notation?

pointfree.io gives us

uncurry (flip flip snd . (ap .) . flip flip fst . ((.) .) . (. (+)) . flip . (((.) . (,)) .) . (+))


which with a little bit of work can be shortened to

uncurry$(flipsnd).((<*>).).(flipfst).((.).).(.(+)).flip.(((.).(,)).).(+)  for 76 bytes. But this still seems really long and complex for such a simple task. Is there any way we can express pairwise addition as a shorter point-free function? To be clear by what I mean by point-free, a point-free declaration of a function involves taking existing functions and operators and applying them to each other in such a way that the desired function is created. Backticks, parentheses and literal values ([],0,[1..3], etc.) are allowed but keywords like where and let are not. This means: • You may not assign any variables/functions • You may not use lambdas • You may not import Here is the same question when it was a CMC • I never understood why it is called “point-free” when it is actually full of points. :P Dec 29, 2017 at 17:10 • It's a shame we're not allowed to import the best Haskell package, or else the solution would just be (+)***(+). Dec 30, 2017 at 3:05 • An idea: (+)<$>(,2)<*>(,4) gives ([1,3],6).
– xnor
Dec 30, 2017 at 5:40
• I spent some time trying to craft a fine solution utilizing xnor's tip... but I ended up with this garbage. I don't even know why I try sometimes... Dec 30, 2017 at 13:59

# 44 bytes

Got this from \x y -> (fst x + fst y, snd x + snd y)

(<*>).((,).).(.fst).(+).fst<*>(.snd).(+).snd


Try it online!

Or, 42 bytes using do:

do a<-fst;((,).(a+).fst<*>).(.snd).(+).snd


Try it online!

# 44 bytes

-8 bytes thanks to Ørjan Johansen. -3 bytes thanks to Bruce Forte.

(.).flip(.)<*>(zipWith(+).)$mapM id[fst,snd]  Try it online! Translates to: f t1 t2 = zipWith (+) (mapM id [fst, snd]$ t1) (mapM id [fst, snd] $t2)  ## 67 bytes -8 bytes thanks to Ørjan Johansen. -1 byte thanks to Bruce Forte. If tuple output is required: (((,).head<*>last).).((.).flip(.)<*>(zipWith(+).)$mapM id[fst,snd])


Try it online!

Yup, me manually doing it doesn't produce ripe fruit. But I am happy with the [a] → (a, a) conversion.

listToPair ∷ [a] → (a, a)
listToPair = (,) . head <*> last
-- listToPair [a, b] = (a, b)


Now if there was a short function with m (a → b) → a → m b.

• Hate to break it to you, but mapM id[fst,snd] is shorter. Dec 30, 2017 at 1:03
• Sadly, mapM id is the golfed version of the function you're probably looking for, sequence. Dec 30, 2017 at 2:06
• Yeah, that's true. I'm just looking at (<*>)'s signature which is m (a → b) → m a → m b. So close... Dec 30, 2017 at 2:08
• There's also Control.Lens.??, which may have been proposed for inclusion in base at some point. Dec 30, 2017 at 2:15
• I want to extract the repeated (.mapM id[fst,snd]) like let r=(.mapM id[fst,snd]) in r(r.zipWith(+)), but I haven't been able to get the typechecker to accept a pointfree version.
– xnor
Dec 30, 2017 at 2:55

# 54 bytes

I honestly doubt that we'll beat @H.PWiz's 44 bytes solution, but nobody was using the fact that (,) implements the type class Functor, so here's another interesting one which isn't too bad:

((<*>snd).((,).).(.fst).(+).fst<*>).flip(fmap.(+).snd)


Try it online!

### Explanation

The implementation of the type class Functor for 2-Tuples are very similar to that of Either (from base-4.10.1.0):

instance Functor ((,) a) where
fmap f (x,y) = (x, f y)

instance Functor (Either a) where
fmap _ (Left x) = Left x
fmap f (Right y) = Right (f y)


What this means for this challenge, is that the following function adds the second elements while keeping the first element of the second argument:

λ f = fmap.(+).snd :: Num a => (a, a) -> (a, a) -> (a, a)
λ f (1,-2) (3,-4)
(3,-6)


So if only we got some little helper helpPlz = \a b -> (fst a+fst b,snd b) we could do (helpPlz<*>).flip(fmap.(+).snd) and would be done. Luckily we have the tool pointfree which gives us:

helpPlz = (ap snd) . ((,) .) . (. fst) . (+) . fst


So by simply plugging that function back in we arrive at the above solution (note that (<*>) = ap which is in base).

# 60 bytes

I'm not seeing any uncurry love here, so I figured I'd pop in and fix that.

uncurry$(uncurry.).flip(.)(flip(.).(+)).(flip(.).((,).).(+))  I thought, with all of the fst and snd, that unpacking the arguments with uncurry might yield some results. Clearly, it was not as fruitful as I had hoped. • uncurry is so verbose. :( But you can replace the outermost parentheses with $. Dec 30, 2017 at 3:39
• Yeah, and that's unfortunately the issue with a lot of function names in Haskell. Just too long for golfing. But thanks for the 1-character savings! Dec 30, 2017 at 5:07