# Modify Rules with new patterns

My Rules always have one instance of TR on the LHS, and several on the RHS, like this

TR[2] -> TR[5] + TR[h, g]


I want to generalize them by adding optional extra arguments in the function TR.

TR[A___, 2, B___] -> TR[A, 5, B] + TR[A, h, g, B]


My current attempt does not match the 2 on the LHS:

Rule[TR[2], TR[5] + TR[h, g]] /.
Rule[lhs_, rhs_] :>
Rule[lhs /. TR[x__] :> TR[A___, x, B___],
rhs /. TR[x__] :> TR[A, x, B]]
(* TR[A$___, x, B$___] -> TR[A$, 5, B$] + TR[A$, h, g, B$] *)


Any idea where to look would be much appreciated.

You've encountered a mechanism in Mathematica called renaming of lexically scoped variables, which you usually don't encounter, unless you start doing ... well, slightly more unusual things. You can read more about it in Preventing \$-renaming of pattern names in lexically-scoped constructs.

The easiest solution is to delay constructing the Rule(Delayed):

rule = TR[2] -> TR[5] + TR[h, g];

rule /.
Rule[lhs_, rhs_] :>
RuleDelayed @@ {lhs /. TR[x__] :> TR[a___, x, b___],
rhs /. TR[x__] :> TR[a, x, b]}
(* TR[a___, 2, b___] :> TR[a, 5, b] + TR[a, h, g, b] *)

1. As you see, instead of directly replacing your Rule with Rule(Delayed), you actually first construct a list, and only then convert it to Rule(Delayed).

2. Since your rule now contains patterns, you probably want to have RuleDelayed, not Rule.

3. I've used lowercase a and b, because you should generally avoid capitalization of symbols.

• Fantastic, so simple. Thanks! Commented Jul 30 at 13:38

Here's a way that keeps the right-hand side from evaluating if using RuleDelayed instead of Rule.

a = 333;
RuleDelayed[TR[2], TR[5] + TR[h, g]] /.
h_[lhs_, rhs_] :>
h @@ { (* <-- call it Domen's trick *)
Hold[lhs] /. TR[x__] :> TR[a___, x, b___],
Hold[rhs] /. TR[x__] :> TR[a, x, b]} /.
h_[Hold[x_], Hold[y_]] :> h[x, y]
(*  TR[a___, 2, b___] :> TR[a, 5, b] + TR[a, h, g, b]  *)


The order is: Restructure the code, then evaluate; only if the code is inside Hold[], it won't evaluate. Hence the a and b in TR[a, x, b] in the replacement for Hold[rhs] are not evaluated. And at the end, when Hold[] is removed, they're either in Pattern[] or the second argument of RuleDelayed, which has the attribute HoldRest; in both cases, they are not evaluated.

If you use Rule, then you want the right-hand side to evaluate (or at least, you don't care if it does):

a = 333;
Rule[TR[2], TR[5] + TR[h, g]] /.
h_[lhs_, rhs_] :> h @@ {
Hold[lhs] /. TR[x__] :> TR[a___, x, b___],
Hold[rhs] /. TR[x__] :> TR[a, x, b]} /.
h_[Hold[x_], Hold[y_]] :> h[x, y]
(*  TR[a___, 2, b___] -> TR[333, 5, b] + TR[333, h, g, b]  <-- oops *)


I guess I must have wanted the a to evaluate on the right but not on the left (Pattern[] is HoldFirst and won't evaluate the pattern symbol). I don't know why I wanted to do that. It doesn't make sense. But there it is.

Now imagine you have something less innocuous than Plus[] on the right-hand side, and you don't want it to evaluate until the code is completely ready. Loose control of evaluation is a source of bugs. It's tricky getting some bits to evaluate and other bits not to. (I hope I've gotten it right here. Where's Leonid?)

• Oh, certainly not my trick ... I saw it in Kuba's comment. Commented Jul 30 at 11:46
• Wow. That's very instructive. Will take you philosophy of defining Pattern symbols beforehand to test for mistakes Commented Jul 30 at 13:40
• @Domen I just wanted to acknowledge you had posted it first for this problem, however you learned it. I've used it and variants for years, and probably learned it here on Mma.SE somewhere, just like I learned to worry about evaluation when giving advice to the world at large. Commented Jul 30 at 14:37