6
$\begingroup$
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.

See also Why does syntax highlighting in `Set` and `Rule` not color pattern names on the RHS?

$\endgroup$
4

4 Answers 4

5
$\begingroup$

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

Further reading: Enforcing correct variable bindings and avoiding renamings for conflicting variables in nested scoping constructs

$\endgroup$
6
  • 1
    $\begingroup$ You can trick it with inactivate too: Module[{x}, f[x_] = x]~Inactivate~Set~Activate~Set $\endgroup$ Commented Jul 2, 2016 at 22:07
  • $\begingroup$ @AlexeyPopkov right, I should rephrase that. $\endgroup$
    – Kuba
    Commented Jul 4, 2016 at 12:49
  • $\begingroup$ @AlexeyPopkov done, thanks again. $\endgroup$
    – Kuba
    Commented Jul 5, 2016 at 7:19
  • $\begingroup$ @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? $\endgroup$
    – masterxilo
    Commented Jul 5, 2016 at 13:24
  • $\begingroup$ @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! $\endgroup$ Commented Jul 5, 2016 at 13:33
3
$\begingroup$

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}
$\endgroup$
4
  • $\begingroup$ 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]] $\endgroup$
    – Ali Hashmi
    Commented Feb 26, 2017 at 18:24
  • $\begingroup$ my bad, I found that clean is indeed required for displaying y=3 in the second part $\endgroup$
    – Ali Hashmi
    Commented Feb 26, 2017 at 18:35
  • 1
    $\begingroup$ @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. $\endgroup$
    – Mr.Wizard
    Commented Feb 26, 2017 at 18:40
  • $\begingroup$ 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 $\endgroup$
    – Ali Hashmi
    Commented Feb 26, 2017 at 20:21
2
$\begingroup$

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[Global`x];
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[Global`x];
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.

$\endgroup$
3
  • $\begingroup$ 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? $\endgroup$
    – Kuba
    Commented Jul 3, 2016 at 8:15
  • $\begingroup$ You have to ClearAll[Global`x] 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. $\endgroup$
    – masterxilo
    Commented Jul 3, 2016 at 15:06
  • $\begingroup$ Ah, right, my mistake, sorry. $\endgroup$
    – Kuba
    Commented Jul 3, 2016 at 15:14
2
$\begingroup$

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]]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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