33

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?

Dr. belisarius
  • 60,527
  • 15
  • 115
  • 190
Mr.Wizard
  • 24,179
  • 5
  • 44
  • 125
  • 2
    A native `Curry` is now available since version 11.3: https://reference.wolfram.com/language/ref/Curry.html – gdelfino Jul 18 '19 at 14:13
  • Just a heads-up: according to those docs `Curry` is being phased out in favor of `CurryApplied` and `OperatorApplied`. – evanb Jun 25 '21 at 07:38

5 Answers5

18

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.

Matt
  • 756
  • 1
  • 9
  • 24
15

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)
Grisha Kirilin
  • 282
  • 2
  • 12
  • Welcome Grisha Kirilin. While this is the same basic method already presented it does indeed look much cleaner. Since I asked this question I've learned quite a bit and I have used a similar form to the one you show in a related application, so I expect this to work, but I need to test it more carefully to be certain. Thank you! By the way, there is now a full StackExchange site dedicated to *Mathematica* users. Please consider [joining us there.](http://mathematica.stackexchange.com/) – Mr.Wizard Jul 19 '12 at 20:45
  • Wow, I didn't expect that somebody would read it. Thanks for your comment. I added a little bit more advanced version it order to function can process combinations like this f[1+1][2+2,6+1]. So I need new account for Mathematica StackExchange. – Grisha Kirilin Jul 19 '12 at 22:21
  • I get a notice when a new answer is posted to one of my questions, and I read all the answers I receive, no matter when they appear. I look forward to seeing you on [Mathematica.SE](http://mathematica.stackexchange.com/) :-) – Mr.Wizard Jul 19 '12 at 22:24
  • By the way: `List @@ ToString /@ Unevaluated /@ Hold[a, b, c]` – Mr.Wizard Jul 19 '12 at 22:41
10

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.

WReach
  • 18,098
  • 3
  • 49
  • 93
  • 1
    I believe that we can use a single symbol `f` in place of `f`,`f1`,`f2` which seems more appropriate. – Mr.Wizard Apr 16 '11 at 15:03
  • @Mr.W Agreed, we could use a single symbol. I was imagining that the `f1` and `f2` might be private symbols, so that the user could not accidentally enter `f[1, 2, 3]` and get a result. Of course, this might be desired behaviour. – WReach Apr 16 '11 at 15:08
  • I see your point. I would have to try it for a while, but I think the second form might even be desirable. – Mr.Wizard Apr 16 '11 at 15:20
6

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.

Leonid Shifrin
  • 22,449
  • 4
  • 68
  • 100
6

There is a way to do it automatically. Consider the function

f[a_, b_, c_] := {a, b, c}

for which we want to make it implicitly "curryable", so it could be called in any of these ways:

f[1, 2, 3]
f[1, 2][3]
f[1][2][3]

This could be achieved if there is a way to generate the following definitions automatically (which we do below):

f[a_, b_, c_] := {a, b, c}
f[a_, b_] := Function[c, f[a, b, c]]
f[a_] := Function[b, Function[c, f[a, b, c]]]

As in the other answer above by Matt, we could have done only one definition: f:=Funcion[a,Function[b,Function[c, BODY]]], but then we will not be able to call f via f[a,b,c] or f[a,b], and will have to call it only as f[a][b] or f[a][b][c]. With multiple definitions we can choose either styles.

Generating these definitions could be done by the function (defined below) CurryableSetDelayed, simply by calling:

CurryableSetDelayed[f[a_, b_, c_], {a, b, c}]

This will work as expected even if any of these symbols is defined, just like SetDelayed would work.

Also, with Notation package you could make it appear as an assignment operator; say f[a_,b_,c]#={c,b,a}, but I didn't try it.

In the source below I use some ad hoc symbols which may conflict with the session, so if you are going to use this, enclose it in a package namespace.

The full code:

ClearAll[UnPattern];
ClearAll[MakeFunction]
ClearAll[CurriedDefinitions]
ClearAll[MyHold]
ClearAll[MyHold2]
ClearAll[CurryableSetDelayed]

SetAttributes[UnPattern,HoldAllComplete];
SetAttributes[MakeFunction,HoldAllComplete];
SetAttributes[CurriedDefinitions,HoldAllComplete]
SetAttributes[MyHold,HoldAllComplete]
SetAttributes[MyHold2,HoldAllComplete]
SetAttributes[CurryableSetDelayed,HoldAllComplete]

UnPattern[x_]:=Block[{pattern},MyHold[x]/. Pattern->pattern/. pattern[v_,_]:>v]

MakeFunction[param_,body_,attrs_]:=With[{p=UnPattern[param],b=UnPattern[body]},
  Block[{function},MyHold[function[p,b,attrs]]/. function->Function]]

CurriedDefinitions[fname_[args__],body_,attrs_]:=MapThread[MyHold2[#1:=#2]&,
  {Rest[(MyHold[fname]@@#1&)/@NestList[Drop[#1,-1]&,{args},Length[{args}]-1]],
   Rest[FoldList[MakeFunction[#2,MyHold[#1],Evaluate[attrs]]&,MyHold[fname[args]],
     Reverse[Drop[{args},1]]]]}]

CurryableSetDelayed[fname_[args__],body_]:={MyHold2[fname[args]:=body],
  Sequence@@CurriedDefinitions[fname[args],body,Attributes[fname]]}
  //. MyHold[x_]:>x/. MyHold2[x_]:>x

Update, now Attributes (HoldAllComplete,etc.) extends to all parameters, so the following works as expected, as long as you set the attributes before calling CurryableSetDelayed:

In[1185]:= ClearAll[f];
SetAttributes[f, {HoldAllComplete}]
CurryableSetDelayed[
  f[a_, b_, c_], {ToString@Unevaluated@a, ToString@Unevaluated@b, 
   Unevaluated@c, Hold@c}];
f[1 + 1, 2 + 2, c + 1]
f[1 + 1, 2 + 2][c + 1]
f[1 + 1][2 + 2][c + 1]

Out[1188]= {"1 + 1", "2 + 2", Unevaluated[c + 1], Hold[c + 1]}

Out[1189]= {"1 + 1", "2 + 2", Unevaluated[c + 1], Hold[c + 1]}

Out[1190]= {"1 + 1", "2 + 2", Unevaluated[c + 1], Hold[c + 1]}
Mohammad Alaggan
  • 3,749
  • 1
  • 28
  • 20
  • I tried your code, but it does not appear that setting `HoldAllComplete` for `f` allows the second and third arguments to be held. It it your intention that they are? – Mr.Wizard Nov 16 '11 at 06:40
  • Thanks for your welcome. To make it work we need to make all the Function[]'s generated inherit the same attributes as f. The only problem I am trying to fix now is that when f has Hold[]'s in its body, my code will interfere with those. I'll update the answer once I find a solution. – Mohammad Alaggan Nov 16 '11 at 07:06
  • The updated code works for "Unevaluated", as in your question, but it won't work if the body contains "Hold" yet. – Mohammad Alaggan Nov 16 '11 at 07:11
  • It works correctly now. The code needs cleaning and I will update it to a cleaner version once it is ready. – Mohammad Alaggan Nov 16 '11 at 07:42