17

Suppose I have the function f with the following definition assigned to it:

f // ClearAll;
f // Attributes = { HoldAll };
f /: HoldPattern[ f[x_] + f[y_] ] := upvalue;

If I evaluate f[x] + f[y], I get upvalue as expected. Now if I instead use the definitions:

f // ClearAll;
f // Attributes = { HoldAll };
f /: HoldPattern[ f[x_] + f[y_] ] := upvalue;
f[x_] := downvalue;

the last one will take precedence, so f[x] + f[y] gives 2 downvalue. From a trace, it's pretty easy to see why this happens, since the second argument isn't seen until after the first has been evaluated:

(* In *)
Column[ Trace[ f[x] + f[y], TraceOriginal -> True ] ]

(* Out *)
f[x] + f[y]
{Plus}
{f[x], {f}, f[x], downvalue}
{f[y], {f}, f[y], downvalue}
downvalue + downvalue
2 downvalue
{Times}
{2}
{downvalue}
2 downvalue

However, I'd like to know if there's good way to prioritize the first definition so I get upvalue instead? A possible workaround would be to do something like this:

(* In *)
Hold[ f[x] + f[y] ] /. UpValues[f] /. DownValues[f] // ReleaseHold

(* Out *)
upvalue

but that's a pretty ugly solution. Anyone have any better ideas?

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
rhennigan
  • 1,783
  • 10
  • 19

3 Answers3

10

Without thinking about any consequences, one idea popped into my mind. First, your definitions for f with the DownValues. I made it a bit more interesting:

ClearAll[f];
f // Attributes = {HoldAll};
f /: HoldPattern[f[x_] + f[y_]] := upvaluesSeen[f[x], f[y]];
f[x_] := downvalue[x]

How about a small wrapper function that temporarily deletes all DownValues of f. With this, the UpValues can act and as soon as we leave the wrapper, we go back to normal. At this point, you should instantly think of Internal`InheritedBlock.

SetAttributes[prioritizeUpvalues, {HoldAll}];
prioritizeUpvalues[expr_, f_Symbol] := Internal`InheritedBlock[{f},
  DownValues[f] =.;
  expr
]

Now, here we go

prioritizeUpvalues[f[x] + f[y], f]
(* upvaluesSeen[downvalue[x], downvalue[y]] *)

or

prioritizeUpvalues[f[x]*f[y], f]
(* downvalue[x] downvalue[y] *)

or

f[3]
(* downvalue[3] *)
halirutan
  • 112,764
  • 7
  • 263
  • 474
8

Adapting Leonid's method for How do you set attributes on SubValues?

ClearAll[f, g];
SetAttributes[{f, g}, HoldAll];
g /: g[x_] + g[y_] := upvalue;
f[x_] := downvalue

f :=
 With[{stack = Stack[_] /. HoldPattern[f] :> g},
   With[{foo = Cases[stack, Alternatives @@ _ /@ First /@ UpValues @ g]},
     g /; foo =!= {}
   ] /; stack =!= {}
 ]

Now:

f[1]
f[2]
f[1] + f[2]
downvalue

downvalue

upvalue

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
4

You can hide your workaround in $Pre:

SetAttributes[specialEvaluate, HoldAll]
specialEvaluate[expr_] := ReleaseHold[
  Hold[expr] /. UpValues[f]
  ]
$Pre = specialEvaluate;

And now:

f[1] + f[2]
(* Out: upvalue *)
C. E.
  • 70,533
  • 6
  • 140
  • 264