As was pointed out in a recent post scoping does not work as expected inside of Module.
An example from that thread is:
Module[{expr},
expr = 2 z;
f[z_] = expr;
f[7]]
(*2 z*)
But the following works almost as expected.
Module[{expr},
expr = 2 z;
Set@@{f[z_], expr};
f[7]]
(*14*)
What language design consideration made wolfram choose this functionality?
Edit: See Jefromi's first comment I changed z from being a local variable to not and forgot to change the output. It does not effect the problem.
Edit2: Michael Pilat's point seems to be that Block and Module have different functions. I think I understand his point, but I think that it is orthogonal to my question. So here is an update.
I can use the following code at the the global level in a notebook:
expr = 2 z;
f[z_] = expr;
f[7]
(*output: 14*)
But when I put the same code block into a Module and make expr local it produces a different output.
Clear[f];
Module[{expr},
expr = 2 z;
f[z_] = expr;
f[7]]
(*output: 2z*)
If you trace the above Module call you find that Set[f[z_], expr] is rewritten to Set[f[z$_,expr]. Now this z->z$ transformation happens on both the lhs and rhs of the Set. It however happens before expr is evaluated, which causes a different result then would be obtained at the global level.
The transformation z->z$ only seems to happen when the rhs has a symbol local to the Module call.
Why does Mathematica choose to have this syntax change in a Module call? What language/implementation design tradeoffs exist here that made this decision.
I think the answer is pretty simple, but subtle: Module
is a lexical scoping construct, and Block
is a dynamic scoping construct.
The Blocks Compared With Modules tutorial from the documentation discusses the distinction:
When lexical scoping is used, variables are treated as local to a particular section of the code in a program. In dynamic scoping, the values of variables are local to a part of the execution history of the program. In compiled languages like C and Java, there is a very clear distinction between "code" and "execution history". The symbolic nature of Mathematica makes this distinction slightly less clear, since "code" can in principle be built up dynamically during the execution of a program.
What
Module[vars, body]
does is to treat the form of the expression body at the time when the module is executed as the "code" of a Mathematica program. Then when any of the vars explicitly appears in this "code", it is considered to be local.Block[vars, body]
does not look at the form of the expression body. Instead, throughout the evaluation of body, the block uses local values for the vars.
It offers this reduced example:
In[1]:= m = i^2
Out[1]= i^2
(* The local value for i in the block is used throughout the evaluation of i+m. *)
In[2]:= Block[{i = a}, i + m]
Out[2]= a + a^2
(* Here only the i that appears explicitly in i+m is treated as a local variable. *)
In[3]:= Module[{i = a}, i + m]
Out[3]= a + i^2
Perhaps the key point is to realize that Module
replaces all instances of i
in the module body with a localized version (e.g., i$1234
) lexically, before any of the body of the module is actually evaluated.
Thus, the module body that's actually evaluated is i$1234 + m
, then i$1234 + i^2
, then a + i^2
.
Nothing is broken, Block
and Module
are intended to behave differently.
According to the documentation, Module
has attribute HoldAll
, which causes everything inside the Module
to remain in an unevaluated state, so your expr
is not evaluated to 2 z
before expr
is assigned to f[z_]
.
Wrapping the second argument to Module
in Evaluate
seems to solve the problem:
In[1]:= Module[{expr}, Evaluate[expr = 2 z;
f[z_] = expr;
f[7]]]
Out[1]= 14
Also, using Block
instead of Module
works:
In[2]:= Block[{expr = 2 z},
f[z_] = expr;
f[7]]
Out[2]= 14
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With