Bug introduced in 2.1 or earlier and persisting through 13.2
UPDATE: Wolfram Support has confirmed that this is a bug.
I noticed that Trace
does the following strange thing (v 13.0.0.0):
Trace[a*a*2, x_ :> test]
(* Out: {test, test} *)
Trace[a*a*2, x_ :> x]
(* Out: {HoldForm[2*a^2], HoldForm[2*a^2], HoldForm[2*a^2]} *)
The first output has two elements, while the second has three. But shouldn't x_
be matched the same number of times in each?
What might be going on? Is this a bug?
Note: the original example used the rule x : Times[___] :> ...
. As MichaelE2 pointed out, this evaluates to x___ :> ...
; in this case, all matched expressions are necessarily exactly one expression long, and so x_
reproduces the same behavior. So, I've modified the example to make it more minimal while still retaining the original character. The responses to this post were made before this edit, and thus may instead use x : Times[___] :> ...
Some more facts:
(maybe this should be moved to the community wiki? will re-evaluate in the morning...)
An even more minimal example: Trace[a*a, x_ :> x]
returns {a^2, a^2}
; Trace[a*a, x_ :> test]
returns {}
.
A discrepancy only seems to occur when either:
the evaluation involves downvalues producing an expression which 1) is composite in some way 2) involves "nontrivial" replacement/substitution of patterns
the evaluation involves rearranging due to an attribute, like
Orderless
orFlat
.
(* easily test for discrepancies—True indicates a discrepancy *)
SetAttributes[DiscrepancyQ, HoldFirst]
DiscrepancyQ[expr_] :=
Module[{test},
Trace[expr, x_ :> test] =!= (Trace[expr, x_ :> x] /. HoldForm[_] -> test)]
(* Note: subvalues behave like downvalues in all respects that
I've tested, but I didn't want to write them all out. *)
(* Discrepancies: *)
(* Orderless: *)
Block[{f},
SetAttributes[f, Orderless];
DiscrepancyQ[f[2,1]]
]
(* Flat: *)
Block[{f},
SetAttributes[f, Flat];
DiscrepancyQ[f[1, f[2]]]
]
(* Downvalues producing "composite" expressions
and involving replacement are bad, and yield True: *)
Block[{f},
f[a_] := {a};
DiscrepancyQ[f[1]]
]
Block[{f},
f[a_] := a[1];
DiscrepancyQ[f[1]]
]
Block[{f, h},
f[a_] := h[a];
DiscrepancyQ[f[1]]
]
Block[{f},
f[a_] := (a;1);
DiscrepancyQ[f[1]]
]
(* No discrepancies: *)
(* Downvalues with no pattern replacement are fine: *)
Block[{f},
f[a_] := 1;
DiscrepancyQ[f[1]]
]
(* Downvalues of these particular "purely extractive" forms are fine: *)
Block[{f},
f[a_] := a;
DiscrepancyQ[f[1]]
]
Block[{f},
f[a_, b_] := a; (* or b *)
DiscrepancyQ[f[1, 2]]
]
(* Upvalues are fine: *)
Block[{a},
a /: f_[a] := {f, a};
DiscrepancyQ[f[1]]
]
(* Ownvalues are fine: *)
Block[{f},
f := {Hold[f], 1};
DiscrepancyQ[f[1]]
]
Trace[a*a*2]
andTrace[a*a*2, x : Times[___] :> HoldForm@x]
both return {a a 2, 2 a a, 2 a^2}. $\endgroup$TracePrint[a*a*2]
shows just two evaluations ofTimes[..]
. TheOrderless
sorting and evaluation2 a a
to2 a^2
is one evaluation; then2 a^2
is evaluated and doesn't change, so evaluation halts. -- Yet another observation:x : Times[___]
evaluates tox___
becauseTimes
has the attributeOneIdentity
. It does not really make a difference, but we should have usedx : HoldPattern[Times[___]]
. $\endgroup$Trace
is introduced in v2. ) $\endgroup$