views:

94

answers:

3

The following code returns 14 as you'd expect:

Block[{expr},
  expr = 2 z;
  f[z_] = expr;
  f[7]]

But if you change that Block to a Module then it returns 2*z. It seems to not matter what other variables besides expr you localize. I thought I understood Module, Block, and With in Mathematica but I can't explain the difference in behavior between Module and Block in this example.

Related resources:

PS: Thanks to Michael Pilat, Davorak, and Bill White for following the scent-trail on this weirdness. Davorak clarifies and gets to the heart of the issue here: http://stackoverflow.com/questions/2739643/why-would-mathematica-break-normal-scoping-rules-in-module

+2  A: 

Have you used Trace on both expressions?

Bill White
Ah, smart! So `z_` is getting turned into `z$_` in the Module version. So I guess that explains it but I still don't understand why that's happening! Maybe I'll update the question though to ask about that weirdness specifically.
dreeves
+3  A: 

First off I think you have exposed a bug here.

Second I think I can offer some insight in to why this is happening, keeping in mind my knowledge of the internals of mathematica are limited.

A statement like: f[z_] := 2 z in Full form is:

SetDelayed[f[Pattern[z, Blank[]]], 2 z]

This sets the DownValue[f] to:

{HoldPattern[f[z_]] :> 2 z}

Then later when an expression, like f[2], is later is evaluated something like following is being preformed:

f[2] /. HoldPattern[f[z_]] :> 2 z

Which would evaluate to 4. Now this is all possible because pattern matching is happening with Pattern[z, Blank[]] from the first code block. This works even if you have perviously set z to a number. In other words.

z = 5;
f[z_] := 2*z

Still produces the same downvalues for f:

{HoldPattern[f[z_]] :> 2 z}

This is possible because Pattern has the HoldFirst Attribute.

The HoldFirst Attribute is not enough protection if you evaluate this inside a Module. Example:

SetAttributes[tmp, HoldFirst];
Module[{expr},
 expr = 2 z;
 tmp[expr]
]

outputs:

tmp[expr$8129]

I propose that because HoldFirst Attribute does not provide immunity to Module's variable rewrite rule that any Pattern in a Rule that contains a local variable have their Pattern variables rewritten. sym->Symbol[SymbolName[sym]~~"$"]

Module[{expr},
 Hold[z_ -> (z; expr)]
]
(*Hold[z$_ -> (z$; expr$1391)]*)

z has be rewritten on both sides of the rule in a simple alpha conversion.

If the rule does not contain a local variable no rewrite happens:

Module[{expr},
 Hold[z_ -> (z)]
]
(*Hold[z_ -> z]*)

Rather then searching to see if a local variable matches a rule variable the above blanket rule is applied.

So the problem is that the local expr is not evaluated before the alpha conversion takes place. Or perhaps even better would be to have expr wrapped in a lazily evaluated alpha conversion which would be required for a RuleDelayed.

This does not happen in Block because Block does not rewrite any of the local variables.

Any other ideas? Any one see any holes in my logic?

Davorak
Thanks so much, Davorak. It seems like you and Michael Pilat are getting at the same issue, right?
dreeves
It is the same issue. The alpha conversion gives scoping to Function, Rule, Set, SetDelayed, but the rewritten variables are still in the global namespace. I guess I was wrong about it being a bug, but it is rather annoying.
Davorak
+5  A: 

I too was a bit surprised by this, but I don't think it's a bug. If you look deep in the examples in the reference page for Module, under the section labeled Possible Issues, there's a little note that says "Variables are renamed in nested scopes" and gives the following example:

In[1]:= Module[{e = Expand[(1 + x)^5]}, Function[x, e]]

Out[1]= Function[x$, e$1194]

In[2]:= %[10]

Out[2]= 1 + 5 x + 10 x^2 + 10 x^3 + 5 x^4 + x^5 

Function is another scoping construct like Module, so x is renamed internally to x$ in the scope of the Function, similar to what you discovered with Trace about z.

In your Module defining f, Set is another such scoping construct, and therefore z is renamed when f is defined inside of a Module, but not when it's inside a Block. Following the advice of that example from the Module documentation, you can build the RHS of your function from its parts to avoid the lexical renaming of the nested scope:

In[3]:= Clear[f, z]

In[4]:= Module[{expr},
  expr = 2 z;
  Set @@ {f[z_], expr};
  f[7]]

Out[4]= 14

HTH!

Michael Pilat
Wow, not what I'd call a pretty work-around but impressive work figuring that out!
dreeves
This is why I come back to SO every day. Learns me some s**t :-).
Timo
I would rather take the unexpected performance hit of having expr wrapped in a lazy alpha conversion rather then dealing with the inconsistent syntax. What language design considerations are going in to play here?
Davorak
This work around is not perfect since f[z_] is no longer held by Set's HoldFirst Attribute. You get a set::write error if you evaluate the example Code twice with out clearing f. Nice catch Michael Pilat.
Davorak
Davorak, right, but you could use instead, for example `Set @@ Hold[f[z_], expr]` to avoid that problem. Or, you could use `f = Function@@{z, expr}` Perhaps we need to start using Perl's TMTOWTDI for Mathematica too =)I can't speak to the exact considerations behind the sub-scope renaming behavior, other than that `f[z_] = ...` is effectively saying you want to treat `z` as a local variable inside the definition of `f`, and the renaming, while sometimes unnecessary, ensures that.
Michael Pilat
Good call on the using Hold instead of list. Variable's name is rewritten to keep make a local scope otherwise know as an alpha conversion like I mentioned in my answer. What I want to know is why does the behavior have to be different at all. They made a call to have the syntax be inconsistant rather then face some other problem. What is that other problem? Which is what I asked over here:http://stackoverflow.com/questions/2739643/why-would-mathematica-break-normal-scoping-rules-in-module
Davorak