How to scope Pattern labels in rules/set?

Module[{x},
f@x_ = x;
p@x_ := x;
{x, x_, x_ -> x, x_ :> x}
]
?f
?p


gives

{x$17312, x$17312_, x_ -> x, x_ :> x}
f[x_]=x
p[x_]:=x


but I'd like to get

{x$17312, x$17312_, x$17312_ -> x$17312, x$17312_ :> x$17312}
f[x$17312_]=x$17312
p[x$17312_]:=x$17312


I thought Module[{x}, body_] operates something like the following, which would do what I want:

module[{x_Symbol}, body_] := ReleaseHold[Hold@body /. x -> Unique@x];
SetAttributes[module, HoldAll];

module[{x},
f@x_ = x;
p@x_ := x;
{x, x_, x_ -> x, x_ :> x}
]
?f
?p


I guess there are some cases with nested scoping constructs that need to be considered for special treatment, but why can't it do the replacement in Set, SetDelayed, Rule, RuleDelayed?

Motivation

I want to usef@x_ = Integrate[y^2, {y, 0, x}] instead of f@x_ := Evaluate@Integrate[y^2, {y, 0, x}] and to be safe I want to scope the variable/pattern label x to something unique.

While Set isn't a scoping construct (SC), it is considered one by other SCs outer to it. ref / Set / Details[[-3]] (thanks to Alexey Popkov for correcting me).

Here it is inner to the Module and Module decides not to interfere in this case (don't know why), but you can trick it:

Module[{x},
Set @@ {f[x_], Integrate[y^2, {y, 0, x}]};
]

?f

f[x$301_]=x$301^3/3

• You can trick it with inactivate too: Module[{x}, f[x_] = x]~Inactivate~Set~Activate~Set Jul 2, 2016 at 22:07
• @AlexeyPopkov right, I should rephrase that.
– Kuba
Jul 4, 2016 at 12:49
• @AlexeyPopkov done, thanks again.
– Kuba
Jul 5, 2016 at 7:19
• @AlexeyPopkov From what I understand, "scoping constructs" are not a "first-class" feature of the system anyways, right? It's only about forwarding correctly renamed (or unchanged), unevaluated symbols to the kernel when a global replacement rule is finally installed... An expression becomes a "scoping construct" when the symbol-renaming processes treat it as such (e.g. by leaving things untouched), no? Jul 5, 2016 at 13:24
• @masterxilo No, definitely scoping constructs like Block, Module and With do not leave things untouched! Only global replacement rules may work in this way, for example if we set the HoldAllComplete attribute for f, then by evaluating f[x_] := g[x] we create a global rule which will do transfer the argument of f to g untouched. Without this attribute it may be evaluated before the transfer. And since the system is based on the replacement rules they definitely are the first-class citizens! Jul 5, 2016 at 13:33

This issue has been discussed before in

Regarding your motivation a solution of mine, which you linked to yourself, is shown in

What is left is to implement a Module alternative as you attempted, or to use some alternative to Rule, Set, etc., as shown in other answers. I shall explore making a Module alternative more robust. To pick apart your starting code:

1. Unique@x will evaluate x; this is unacceptable.

2. The local Symbol lacks the Temporary attribute and will not be garbage-collected.

3. Only a single local Symbol may be specified.

4. There is no provision for assignments within the first parameter.

Here is my attempt to fix these limitations.

SetAttributes[module, HoldAll]

clean = Replace[#, (Set | SetDelayed)[s_Symbol, _] :> s, {2}] &;

module[{sets : (_Symbol | _Set | _SetDelayed) ..}, body_] :=
(List @@ #;
Unevaluated[body] /.
List @@ MapAt[HoldPattern, {All, 1}] @
Thread[clean[Hold[sets] :> #], Hold]) & @ Module[{sets}, Hold[sets]]


Now:

x = 1;
module[{x},
f @ x_  = x;
p @ x_ := x;
{x, x_, x_ -> x, x_ :> x}
]
?f
?p

{x$533, x$533_, x$533_ -> x$533, x$533_ :> x$533}

f[x$533_]=x$533

p[x$533_]:=x$533


And also:

module[{x, y = 3, z := Print["foo!"]},
{x, y, x_, y_, x_ -> x y, x_ :> x, z_ :> x y}
]

{x$533, 3, x$533_, y$533_, x$533_ -> 3 x$533, x$533_ :> x$533, z$533_ :> x$533 y$533}

• please correct me if i am wrong but the following code works without having to use clean i.e. Unevaluated[body] /. List @@ (MapAt[HoldPattern, {All, 1}]@Thread[Hold[sets] :> #, Hold] &)@ Module[{sets}, Hold[sets]] Feb 26, 2017 at 18:24
• my bad, I found that clean is indeed required for displaying y=3 in the second part Feb 26, 2017 at 18:35
• @AliHashmi I am pleased that you are not only reading my answers but looking for ways to correct and improve them. :-) Please don't hesitate to make suggestions like this. It will be good to work through them together. Feb 26, 2017 at 18:40
• thank you. actually i realized how blatantly wrong i was yet again. The clean function was to get the l.h.s symbols from the Set and DelayedSet definitions and then the thread is applied to get the rule x to x$something under Hold Feb 26, 2017 at 20:21 I think a third kind of behaviour would also be possible: it is debatable whether f@x_ = x, x_ -> x within Module[{x}, ... should not become f@x_ = x$123, x_ -> x$123 because then the sequence ClearAll[Globalx]; x = 1; 0 /. x_ -> x  would do the same whether executed in the global context or within a Module[{x}, ...]. It currently does not: With a fresh kernel (or after clearing x), the program gives 1 in the global context and 0 within a Module[{x}, ...]: ClearAll[Globalx]; Module[{x}, x = 1; 0 /. x_ -> x]  Use Hold, Unique, ReleaseHold as you demonstrated if you want some specific renaming to happen within pattern labels of Set, SetDelayed, Rule, RuleDelayed - Module will not descend into these by design. • I get 1 in Module and in Global  context. What version are you using? Moreover, I think that would be really unexpected: f@x_ = x$123 but why not f[ x$123_ ]= x$123? What do you think?
– Kuba
Jul 3, 2016 at 8:15
• You have to ClearAll[Globalx] before running it in the Module. I am arguing for what you suggested, f[ x$123_ ]= x$123 in the OP. But f@x_ = x\$123 would also make sense, because at the moment, ClearAll[Globalx, Globalf]; x = 1; f@x_ = x; f@0 gives 1, but ClearAll[Globalx, Globalf]; Module[{x}, x = 1; f@x_ = x;]; f@0 gives 0, just like with the Rule example I posted here. Jul 3, 2016 at 15:06
• Ah, right, my mistake, sorry.
– Kuba
Jul 3, 2016 at 15:14

It looks like what you need is the LocalPatterns package by Ted Ersek. It introduces new special symbols DotEqual and LongRightArrow which works like Set and Rule but provide variable scoping during evaluation of the RHS:

<< LocalPatterns.m

?DotEqual


lhs\[DotEqual]rhs evaluates rhs using a local environment for any variable used as a pattern in lhs. From then on lhs is replaced by the result of evaluating rhs whenever lhs appears. The infix operator \[DotEqual] is entered as \[DotEqual]. The expression lhs\[DotEqual]rhs, has an equivalent form LocalSet[lhs,rhs].

Clear[f];
x = 53.54;
f[x_] \[DotEqual] Integrate[Log[Sqrt[x] + 1], x]

Definition[f]


f[x_] := Sqrt[x] - x/2 + (-1 + x) Log[1 + Sqrt[x]]

?LongRightArrow


lhs → rhs represents a rule where rhs is evaluated immediately using a local environment for any variable used as a pattern in lhs. The infix operator → is entered as \[LongRightArrow]. The expression lhs → rhs has an equivalent form LocalRule[lhs,rhs].

x = 34/7;
g2 = g[x_]\[LongRightArrow]Integrate[Log[Sqrt[x] + 1], x]

g[x_] :> Sqrt[x] - x/2 + (-1 + x) Log[1 + Sqrt[x]]
`