11

I don't understand why $a$ and $b$ are not local in the following code:

Clear[lint];
lint[f_, x_] := Module[{a, b}, f /. Replace[x, {a_, b_} -> a -> b]];

Clear[a];
lint[x, {x, t}] (* returns t, as I would expect *)

a = 3;
lint[x, {x, t}] (* returns x *)

Clear[a]; a=3;
lint1[f_, x_] := Module[{a, b}, f /. Replace[x, {a_, b_} :> a -> b]]; (* returns t *)

It appears that the occurrence of $a$ in the pattern inside the replace is being interpreted in the outer scope. Why is that?

Update: Having read @szabolcs comment and the first answer, I still don't quite understand. First, the documentation for Rule says that "Symbols that occur as pattern names in lhs are treated as local to the rule.". Fine, that's what the comment said. But if they are local, then why is $3$ substituted for $a$ in the second example above?

Second, why does using :> solve the problem? The only difference between Rule and RuleDelayed, as far as I can see, is when the right-hand symbols are evaluated. So why does this change the behavior? (See the new fourth example above).

rogerl
  • 4,209
  • 3
  • 27
  • 42
  • 4
    Your use of Module is unnecessary. If you use RuleDelayed instead of Rule, then the pattern variables are automatically localized. lint2[f_, x_] := f /. Replace[x, {a_, b_} :> a -> b] – rm -rf Feb 15 '14 at 16:10
  • 1
    The question is "why", not how to fix.. – george2079 Feb 15 '14 at 16:30
  • 2
    No time for a full answer, but in short: Rule itself is a sort of scoping construct in Mathematica and it will protect symbols that are used as pattern names in its LHS from being renamed by Module. – Szabolcs Feb 15 '14 at 16:34
  • @szabolcs I updated my question to respond to your comment --- there are still some things here that don't make sense, even in light of what you say about Rule. – rogerl Feb 18 '14 at 02:37
  • 9
    If using version 9, might try setting SetSystemOptions["StrictLexicalScoping"->True]` and see if that changes things in a useful way. – Daniel Lichtblau Feb 18 '14 at 03:02
  • 1
    Regarding the update: The difference is that with Rule the symbol a does not appear on the rhs, because the rhs is evaluated before Rule "sees it". So the rule is: {a_, b_} -> (3 -> b). Since the symbols a and b occur as pattern names in the lhs of this rule, they are indeed treated as local to the rule, but this localisation happens after the evaluation of the rhs. RuleDelayed has the HoldRest attribute, so the rhs doesn't get evaluated and the rule acts as you expect. – Simon Woods Feb 18 '14 at 13:41
  • 2
    @rogerl Yes, I agree with you that this is weird, confusing, non-obvious, and probably a fault of Mathematica ... Here's a clearer example: a = "outside"; Module[{a = "inside"}, 1 /. a_Integer -> a]. This one returns "outside", not "inside". We told you that both Module and Rule do some kind of localization. But the end result is that a doesn't get localized at all and it's evaluated to its global value. Of course this is extremely confusing! But it's how the system works (i.e. doing "localization" via renaming) and we cannot change it. – Szabolcs Feb 18 '14 at 18:07
  • 1
    Just so you don't get the impression that no one else finds this behaviour upsetting and user-unfriendly ;-) We learned to live with it. – Szabolcs Feb 18 '14 at 18:08
  • @DanielLichtblau Does that option have any known bad side-effects? I assume there might easily be some internally used code that will get broken if we use it ... – Szabolcs Feb 18 '14 at 18:09
  • 3
    @rogerl As for the why, the main reason is that Module does localization by renaming variables before evaluation, as you can see by evaluating Module[{x}, x]. However, it does not rename everything. Things inside other scoping constructs inside Module do not always get renamed. I do not fully understand when this renaming happens and when it doesn't, nor why (and when) it is necessary. It a very dark corner of Mathematica. The rest is explained by the answers: since Rule is a kind of scoping construct, it prevents Module from renaming a, so we still have a instead ... – Szabolcs Feb 18 '14 at 18:19
  • 3
    ... of a$123 (i.e. the "localized" version). a is a global symbol which has a value, to which it gets evaluated. Again the thing to pay attention to here is that there aren't really any local and global variables in Mathematica, only renamed or non-renamed ones. (Side note: What Block does is that it temporarily removes the definitions associated with symbols, but the symbols themselves stay the same.) – Szabolcs Feb 18 '14 at 18:21
  • @szabolcs Thanks. I think your explanation is the clearest and most complete, but I can't accept it because it's not in the form of an Answer. Can you make it so? And yes, this seems pretty ugly. But at least now I understand what is happening. – rogerl Feb 18 '14 at 20:26
  • 1
    @Szabolcs Unknown as to what it might do. I always have it enabled for more than two years and have seen no ill effects at all in my code. We believe it cannot be enabled for building Wolfram|Alpha since that seems to correlate with a crash problem. But the details have yet to be sorted out. – Daniel Lichtblau Feb 18 '14 at 20:54

1 Answers1

13

As Szabolcs say in comments, "Rule itself is a sort of scoping construct in Mathematica and it will protect symbols that are used as pattern names in its LHS from being renamed by Module." Let us check this:

Clear["`*"];
Module[{a, b}, {a_, b_} -> a -> b]

(* =>  {a_, b_} -> a -> b *)

One can see that Module has not renamed a and b as Szabolcs state. But this protection takes place only if both variables on LHS are defined via Blank (Mathematica 8.0.4):

Clear["`*"];
Module[{a, b}, {a_, b} -> a -> b]

(* => {a$_, b$96} -> a$ -> b$96 *)

Here we see a limitation of Module's cleverness...

Alexey Popkov
  • 61,809
  • 7
  • 149
  • 368
  • This is clear in the Module docs.. Module substitutes new symbols for each of the local variables that appear anywhere in expr except as local variables in scoping constructs. -- still a bit disturbing – george2079 Feb 15 '14 at 19:41
  • 1
    @george2079 Could you explain the second example in my answer? – Alexey Popkov Feb 16 '14 at 00:57