One may implement a limited form of Currying in Mathematica, using this construct:
f[a_][b_][c_] := (a^2 + b^2)/c^2
Allowing one to do, for example:
f[4][3] /@ Range@5
{25, 25/4, 25/9, 25/16, 1}
There is a problem: Attributes
only apply to the first (set of) argument(s). Consider:
ClearAll[f]
SetAttributes[f, HoldAllComplete]
f[a_][b_][c_] :=
{ToString@Unevaluated@a,
ToString@Unevaluated@b,
ToString@Unevaluated@c}
f[2 + 2][ 8/4 ][3 + 5]
{"2 + 2", "2", "8"}
My intent was to return "8 / 4"
and "3 + 5"
in the list.
Consequently:
Is there a way to extend attributes to this construct?
Is there another convenient construct to achieve this?
Are there other ways, besides attributes, to extend Currying within Mathematica?
I don't think there is any way to have attributes apply to the later parts of such an "upvalue" pattern definition.
One alternative is to use pure functions with attributes. Not as convenient as pattern matching, but when you evaluate f[2+2][8/4]
, it actually gives a result Curry would have liked. ("Function" is Mathematica's "lambda", if you're familiar with lambda calculus.)
f = Function[a,Function[b,Function[c,HoldForm@{a,b,c},HoldAll],HoldAll],HoldAll]
I presume you want to be able to do something like the following:
f[2+2][2/1] /@ Unevaluated@{1+1,3+3}
→ {{2+2, 2/1, 1+1}, {2+2, 2/1, 3+3}}
If you're going to do this sort of thing often, you can make it slightly easier to type in:
hf[args_,body_]:=Function[args,body,HoldAll]; SetAttributes[hf,HoldAll];
f = hf[a, hf[b, hf[c, HoldForm@{a, b, c}]]]
The Mathematica Cookbook presents a rather different approach to Currying on pages 73-77.
As a general guideline, if you try to control when Mathematica evaluates expressions, you will make yourself miserable. A better approach in many situations is to use symbols as placeholders for the expressions you don't want evaluated yet, and then when the time comes to evaluate one, you can substitute the desired expression for the symbol.
Sorry for a probably unrelated comment. I just searched «currying with Mathematica» and this question was the first in Google list. Although, it is 1 year old and already got answer, I found that the solutions presented are not quite elegant imho. The simple modification of the initial code should be as follows:
ClearAll[f]
SetAttributes[f, HoldAllComplete]
f[a_, b_, c_] := {ToString@Unevaluated@a, ToString@Unevaluated@b,
ToString@Unevaluated@c}
f[a__] := Function[x, f[a, x], HoldAll]
It results in the desired carrying:
f[2+2][2+1] /@ Unevaluated@{1+1, 3+3}
→ {{2+2, 2+1, 1+1}, {2+2, 2+1, 3+3}}
It works fine for three possible partitions of arguments
f[1 + 1, 2 + 2, 6 + 1]
f[1 + 1, 2 + 2][6 + 1]
f[1 + 1][2 + 2][6 + 1]
and gives the correct result:
{"1+1", "2+2", "6+1"}}
, but it fails for f[1 + 1][2 + 2, 6 + 1]
. For this one, one can use a little bit more advanced version:
ClearAll[f, g]
SetAttributes[f, HoldAllComplete]
SetAttributes[g, HoldAllComplete]
f[a_, b_, c_] := (ClearAll[g]; SetAttributes[g, HoldAllComplete];
Thread[Hold[{a, b, c}]] /. {Hold[e_] :> ToString@Unevaluated[e]})
f[a__] := (g[x__] := f[a, x]; g)
I'm not aware of any way to extend attributes to the second or later curried argument lists -- although I'd love to hear about one.
You can implement definitions for expressions with the same appearance as the curried expression by using pure functions (although I would hesitate to call it "convenient"):
ClearAll[f, f1, f2]
SetAttributes[{f, f1, f2}, HoldAllComplete]
f[a_] := Function[b, f1[a, b], HoldAllComplete]
f1[a_, b_] := Function[c, f2[a, b, c], HoldAllComplete]
f2[a_, b_, c_] :=
{ ToString@Unevaluated@a
, ToString@Unevaluated@b
, ToString@Unevaluated@c
}
f[2+2][8/4][3+5]
It is possible to pattern match a curried expression using the now undocumented symbol HeadCompose
:
In[65]:= MatchQ[g[x][y][z], HeadCompose[g, x_, y_, z_]]
Out[65]= True
... although this capability does not help in the matter at hand. HeadCompose
was deprecated a few versions ago, to the point of it finally being removed from the documentation. But I am not aware of any other way to pattern-match curried expressions. I speculate that it was deprecated precisely because one cannot effectively attach attributes and definitions to it, giving it that dreaded status: This symbol has not been fully integrated into the long-term Mathematica system, and is subject to change.
Coming late to the party - so not a direct answer to the question (which has been answered by other posts quite well). I just want to point out that one can have a form of non-local control over the evaluation by using Stack
and exceptions. It is a bit ugly, but I don't think it has been fully explored. Here is an example:
ClearAll[f];
f := With[{stack = Stack[_]},
With[{fcallArgs =
Cases[stack, HoldForm[f[x_][y_][z_]] :> Hold[x, y, z]]},
Throw[First@fcallArgs] /; fcallArgs =!= {}]];
In[88]:= Catch[f[2+2][8/4][3+5]]
Out[88]= Hold[2+2,8/4,3+5]
This uses the fact that heads are evaluated before elements, recursively. What you can see from here is that one is able to extract the unevaluated arguments in this way, and can, perhaps, use them in further processing. The computation is interrupted though. It should also be possible to extract enough information from the Stack[_]
to resume the computation. I am not sure whether one can implement continuations in Mathematica, but if so, that should be probably along these lines.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With