17
$\begingroup$

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?

$\endgroup$
10
$\begingroup$

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] *)
$\endgroup$
8
$\begingroup$

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
$\endgroup$
4
$\begingroup$

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 *)
$\endgroup$
  • 1
    $\begingroup$ A caveat: this only works on user input rather than expressions generated internally. $\endgroup$ – Mr.Wizard Sep 21 '15 at 0:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.