Operator currying: how to convert f[a,b][c,d] to {a+c,b+d}?

This question is related to this golfing question (but I'm not interested in golfing, only in functional operator composition):

How can we convert f[a,b][c,d] to {a+c,b+d} using only operator forms and function compositions?

So far I've figured out two ways of converting f[a][b] to a+b:

Apply[Curry[Plus]] ~ Operate ~ f[a][b]
(*    a + b    *)

Plus @@ Apply[Curry[List]] ~ Operate ~ f[a][b]
(*    a + b    *)

but I'm stumped by the double-argument forms of the first problem, which interferes with currying. Do you know how to solve this?

Both Operate and Curry don't have access to the full expression, acting mostly on the head. This is why both of them take the depth as an optional argument, to know where to stop, since there is no other way for them to know - their modus operandi is limited to a single interation of evaluation sequence.

What you can do is something like this:

curry[expr_, _, 0]:=expr;

curry[head_Symbol, combiner_, n_:2][args___] :=

curry[head_[prev___], combiner_, n_][args___] :=
curry[head[prev, combiner[args]], combiner, n-1]

which then can be used as:

Operate[Apply[curry[List, Plus]], f[a, b][c, d]]

(* {a + b, c + d} *)

But in a more complex case like e.g. f[a, b][c, d][e, f], you would have to manually set the depth for both Operate and curry:

Operate[Apply[curry[List, Plus, 3]], f[a, b][c, d][e, f], 2]

(* {a + b, c + d, e + f} *)

Not sure how much this takes it away from the operator form paradigm you fancy, but I don't how this could be done much differently, without using some hacks (such as using the Stack), which essentially would still serve to get a hold on entire expression rather than just its left-most head.

• A two-argument curry, wow this is really good. Makes me wonder why Curry isn't more flexible. Needing to specify the depth, even twice, is of course a hack; but your answer already teaches me a lot. Thank you Leonid! – Roman Jun 15 at 17:07
• To get the order of the summation right, we can do Plus @@ Operate[Apply[curry[List,List]], f[a,b][c,d]] to get the desired {a+c, b+d}. For larger terms, Plus @@ Operate[Apply[curry[List,List,4]], f[a,b][c,d][e,f][g,h], 3] gives {a+c+e+g, b+d+f+h}. This usage invites the definition of listCurry[n_: 2] := curry[List, List, n] as a curried-argument-to-list-of-lists converter helper: Plus @@ Operate[Apply[listCurry[]], f[a,b][c,d]] then gives the desired {a+c, b+d}. Alternatively, make default values for the first two arguments of curry be List. – Roman Jun 15 at 18:58
• Just to give some examples of usage for Leonid's two-argument curry operator: curry[F,G][a,b,c][d,e,f] gives F[G[a,b,c], G[d,e,f]]. The function F wraps the whole expression, and each set of arguments is wrapped in G. A larger example is curry[F,G, 4][a,b][c,d][e,f][g,h] giving F[G[a,b], G[c,d], G[e,f], G[g,h]]. – Roman Jun 15 at 20:25
• Leonid, I don't think it's a good idea to auto-detect the depth using Stack or similar. What if in the last example of the previous comment I wanted to curry only the first three sets of arguments? curry[F,G, 3][a,b][c,d][e,f][g,h] correctly returns F[G[a,b], G[c,d], G[e,f]][g,h] and leaves the last argument set [g,h] alone. Auto-detection of the depth wouldn't be able to handle this case. – Roman Jun 15 at 20:28
• @Roman Sure, autodetection flies in the face of operator approach, since an operator by itself knows nothing about the depth / order. You can default the combiner to List, sure. – Leonid Shifrin Jun 15 at 21:10