Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would Mathematica break normal scoping rules in Module?

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.

like image 374
Davorak Avatar asked Apr 29 '10 18:04

Davorak


2 Answers

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.

like image 52
Michael Pilat Avatar answered Nov 02 '22 02:11

Michael Pilat


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
like image 30
Isaac Avatar answered Nov 02 '22 03:11

Isaac