18
\$\begingroup\$

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$(`flip`snd).((<*>).).(`flip`fst).((.).).(.(+)).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

\$\endgroup\$
12

4 Answers 4

11
\$\begingroup\$

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!

\$\endgroup\$
9
\$\begingroup\$

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.

\$\endgroup\$
6
  • 3
    \$\begingroup\$ Hate to break it to you, but mapM id[fst,snd] is shorter. \$\endgroup\$ Dec 30, 2017 at 1:03
  • \$\begingroup\$ Sadly, mapM id is the golfed version of the function you're probably looking for, sequence. \$\endgroup\$ Dec 30, 2017 at 2:06
  • \$\begingroup\$ Yeah, that's true. I'm just looking at (<*>)'s signature which is m (a → b) → m a → m b. So close... \$\endgroup\$ Dec 30, 2017 at 2:08
  • 1
    \$\begingroup\$ There's also Control.Lens.??, which may have been proposed for inclusion in base at some point. \$\endgroup\$ Dec 30, 2017 at 2:15
  • \$\begingroup\$ 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. \$\endgroup\$
    – xnor
    Dec 30, 2017 at 2:55
4
\$\begingroup\$

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

\$\endgroup\$
4
\$\begingroup\$

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.

\$\endgroup\$
2
  • 2
    \$\begingroup\$ uncurry is so verbose. :( But you can replace the outermost parentheses with $. \$\endgroup\$ Dec 30, 2017 at 3:39
  • \$\begingroup\$ 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! \$\endgroup\$ Dec 30, 2017 at 5:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.