Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Currying with Mathematica

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?

like image 612
Mr.Wizard Avatar asked Apr 16 '11 12:04

Mr.Wizard


4 Answers

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.

like image 189
Matt Avatar answered Nov 19 '22 17:11

Matt


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)
like image 39
Grisha Kirilin Avatar answered Nov 19 '22 15:11

Grisha Kirilin


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.

like image 10
WReach Avatar answered Nov 19 '22 16:11

WReach


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.

like image 6
Leonid Shifrin Avatar answered Nov 19 '22 16:11

Leonid Shifrin