MathGroup Archive 2010

[Date Index] [Thread Index] [Author Index]

Search the Archive

Re: Scoping with Module

  • To: mathgroup at smc.vnet.net
  • Subject: [mg109595] Re: Scoping with Module
  • From: Leonid Shifrin <lshifr at gmail.com>
  • Date: Fri, 7 May 2010 06:29:48 -0400 (EDT)

Hi Patrick,

My understanding is that Mathematica uses aggressive variable renaming (more
than is strictly necessary for many cases) in case it sees nested scoping
constructs with conflicting variables (variables with the same name).  This
manifests itself in the renaming happening if these variables
occur not just in the declaration lists, but also in the body, like here:

scoping1[{a},scoping2[{b},a+b]

In such cases, <b> will be often renamed to something like b$ even before
the actual binding happens,
for example:

In[1]:= Function[a, Function[b, Print[a + b]]][c]

Out[1]= Function[b$, Print[c + b$]]

Note that in many cases this is not strictly necessary, but it is probably
easier and / or more efficient to do this excessive renaming than to work on
the case-by-case basis. Now, both Set with patterns and Module are scoping
constructs. In your starting examples there are no local variables in the
Module declaration , so no conflicts of the above type are possible.
However, in this one:

Module[{r},
 r = {t :> 1};
 f[t_] = t /. r;
 DownValues[f]
 ]

you do have a possibility of a conflict of the above type, and so
Mathematica renames a dummy variable in the inner scoping construct - <t>
in   f[t_] = t /. r; in this case. At the same time, <t> in the
r = {t :> 1}; remains global since no patterns / scoping is involved here -
thus the result you observed. In your final example:

Module[{r},
 r = {t :> 1};
 r2 = r;
 f[t_] = t /. r2;
 DownValues[f]
 ]

while the situation looks pretty similar from the evaluation viewpoint, <r>
does not appear inside the definition f[t_] = t /. r2; lexically (this
happens only dynamically), and for lexical scoping constructs (at least as
they exist in Mathematica) this makes all the difference. At the time of
variable name conflict resolution (this is before any code inside Module
starts to execute), Mathematica can no longer sense any danger, and thus no
renaming happens.

I don't know whether this behavior should be sonsidered satisfactory or not,
since it is certainly not very intuitive. I'd  say that it's still ok: when
we take a line like f[t_] = t /. r;, we should treat <t> as a local
lexically scoped variable (the fact that it evaluates to global <t> is
related to evaluation, not scoping). Therefore, there is generally no
guarantee that injecting there r = {t :> 1}; (which is defined outside the
scope of Set in f[t_] = t /. r;) will work. I think that our puzzlement
originates from mixing scoping and evaluation. From the fact that Set
evaluates the r.h.s and uses global values of <t> for entries of <t> on the
r.h.s. (this is what we are used to from our experiences of working with
global variables), we subconsciously expect that  this is true for all
entries of <t> on the r.h.s, while this is only guaranteed for non-lexical
entries of <t> - those that were not explicitly (lexically), but rather
dynamically, present in the code before evaluation started. The problem is
made worse by the fact that in many common cases  it does work  (like the
case of global variables and no surrounding scoping constructs).

The bottom line is that IMO the construct you looked at is not reliable from
the start in that it attempts to do scope engineering and the results are
then necessarily dependent on the details of Mathematica internal
implementation of lexical scoping constructs. Then it is probably a matter
of taste whether or not to use constructs like this. Personally, I do it
from time to time for my own purposes, but I would hesitate to put them into
"production" code. Besides, I believe that in most cases there  exist more
reliable alternatives.

Regards,
Leonid



On Thu, May 6, 2010 at 12:52 PM, Patrick Scheibe <
pscheibe at trm.uni-leipzig.de> wrote:

> Hi,
>
> I cannot explain the following behaviour of Module. Maybe someone else
> can:
>
> in the global scope
>
> f[t_] = t;
> DownValues[f]
>
> results in {HoldPattern[f[t_]] :> t} as expected. Simply wrapping it
> with Module, without
> making f lokal does not change this behaviour.
>
> Module[{},
>  f[t_] = t;
>  DownValues[f]
>  ]
>
> results still in {HoldPattern[f[t_]] :> t}.
> Say I want to replace the right side of the definition with a rule
>
> r = {t :> 1};
> f[t_] = t /. r;
> DownValues[f]
>
> and we get {HoldPattern[f[t_]] :> 1}
>
> Module[{},
>  r = {t :> 1};
>  f[t_] = t /. r;
>  DownValues[f]
>  ]
>
> still the expected result {HoldPattern[f[t_]] :> 1}
>
> Now (sorry for the lengthy introduction) making r lokal
>
> Module[{r},
>  r = {t :> 1};
>  f[t_] = t /. r;
>  DownValues[f]
>  ]
>
> and we get {HoldPattern[f[t$_]] :> t$}. The Pattern t_ was renamed
> before the replacement could act.
> Using the local variable r to set a global r2 and it's working again.
>
> Module[{r},
>  r = {t :> 1};
>  r2 = r;
>  f[t_] = t /. r2;
>  DownValues[f]
>  ]
>
> So there must be at least one thing about Module I did'nt understand.
> I thought the main difference between Block
> and Module is the lexical scoping!? But the only thing which was
> renamed is the lokal usage of r.
> Any ideas?
>
> Cheers
> Patrick
>
> 7.0 for Mac OS X x86 (64-bit) (February 19, 2009)
>
>



  • Prev by Date: Re: Optimization problem for dice game (repost)
  • Next by Date: Re: Giving several functions the same argument
  • Previous by thread: Scoping with Module
  • Next by thread: Re: Scoping with Module