11

Consider a function that sorts its arguments based on a function called order

Clear[f, order]
f[x_, y_] /; order[x] > order[y] := f[y, x]
order[1] = 1;
order[2] = 2;
expr1 = f[1, 2]
expr2 = f[2, 1]
(* f[1, 2] *)
(* f[1, 2] *)

However, let's imagine that we change the ordering function

order[1] = 2;
order[2] = 1;

Surprisingly, expr1 and expr2 still evaluate to f[1,2]

expr1
expr2
(* f[1, 2] *)
(* f[1, 2] *)

However, if we introduce f[1, 2] we obtain the correct result

f[1, 2]
(* f[2, 1] *)

Is there a way to force the reevaluation of expr to obtain f[2, 1]? I am looking for a function along the lines of

ForceEvaluation@expr1
ForceEvaluation@expr2
(* f[2, 1] *)
(* f[2, 1] *)
J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
AGim
  • 133
  • 6
  • 1
    While I appreciate the accept, I'd recommend holding off for at least a few more hours. There are plenty of users who know more about Mathematica than I who might be able to provide a better answer, but once an answer is accepted many people will start skipping the question entirely. – eyorble Dec 19 '18 at 18:12
  • Thanks for the comment, since I am new I did not consider this. It is certainly an obscure behaviour of Mathematica, and it would be great to hear more about it! – AGim Dec 19 '18 at 18:17
  • 1
    Related, but not as minimal an example: (139461) – Mr.Wizard Dec 19 '18 at 21:26

1 Answers1

14

Stumbled across the solution to this in tutorial/ControllingInfiniteEvaluation in the documentation. The last paragraph says:

Some of the trickiest cases occur when you have rules that depend on complicated /; conditions (see "Putting Constraints on Patterns"). One particularly awkward case is when the condition involves a "global variable". The Wolfram Language may think that the evaluation is finished because the expression did not change. However, a side effect of some other operation could change the value of the global variable, and so should lead to a new result in the evaluation. The best way to avoid this kind of difficulty is not to use global variables in /; conditions. If all else fails, you can type Update[s] to tell the Wolfram Language to update all expressions involving s. Update[] tells the Wolfram Language to update absolutely all expressions.

The symbol to updated in this case is f, rather than order, but:

Clear[f, order]
f[x_, y_] /; order[x] > order[y] := f[y, x]
order[1] = 1;
order[2] = 2;
expr1 = f[1, 2];
expr2 = f[2, 1];
{expr1, expr2}

{f[1, 2], f[1, 2]}

order[1] = 2;
order[2] = 1;
Update[f];
{expr1, expr2}

{f[2, 1], f[2, 1]}

Clunky, but it works.

eyorble
  • 9,383
  • 1
  • 23
  • 37