Re: Re: Re: scope all wrong? in Mathematica 4.1
- To: mathgroup at smc.vnet.net
- Subject: [mg31947] Re: [mg31914] Re: Re: scope all wrong? in Mathematica 4.1
- From: David Withoff <withoff at wolfram.com>
- Date: Thu, 13 Dec 2001 01:08:56 -0500 (EST)
- Sender: owner-wri-mathgroup at wolfram.com
> I think that ideal behavior would be to issue a warning "argument of > function is shadowed by local variable, will have no effect" I would go along with that. Probably the only objection would be that adding such a message may not be worth the effort, since essentially no on ever makes this mistake anyway. I found the next comment to be fairly interesting: > To barge right in and replace x in the body of the Module when > this x should refer to the local variable strikes me as too simplistic and > is therefore a surprise. Does this mean that the behavior was surprising because it wasn't sufficiently complicated? The only times that I have been confused by Mathematica was when I expected it to be more complicated than it really was. Mathematica is actually a pretty simple system. The two different ways of handling this situation can be illustrated by comparing: In[1]:= f[x_]:=Module[{x},x=5;Print["x is ", x]] In[2]:= f[99] Module::lvsym: Local variable specification {99} contains 99 which is not a symbol or an assignment to a symbol. Out[2]= Module[{99},99=5;Print[x is ,99]] where, as you noted, the value associated with x in f[x_] just goes barging right in to the right side of the rule, and In[3]:= f = Function[{x},Module[{x},x=5;Print["x is ",x]]]; In[4]:= f[99] x is 5 where the value associated with the x in Function[{x}, ...] respects the local variable of same name in the Module. So there really are two qualitatively different ways of handling this situation. The decision on how to handle this in Mathematica was not made lightly. Among the many considerations was the observation that most people think of applying a rule as a qualitatively different operation than evaluation of nested scoping constructs, and in particular expect things like With[{x = 99}, Module[{x}, ...]] to be handled differently than things like f[99] /. f[x_] :> Module[{x}, ...]. People who expect rule application to be handled like another scoping construct may very well be disappointed by this design choice. I'm not sure it is possible to design a programming language that matches everyone's expectations. I would claim, however, that there is ample precedent in other programming languages for this aspect of the design of Mathematica, that there is no absolute principle for choosing one design or the other, and that in some sense it comes down to choosing what will be most useful to most users most of the time. > I tried in my previous post to express what I think would have to be > changed to accommodate "ideal" behavior. In essence, some processing (and > therefore unHolding) of the RHS of := -- to determine the locals and look > for name clashes, and if neccessary preempt the pattern matcher -- would > have to occur before the value that the system has determined for the > pattern x_ on LHS is passed in to the RHS. Currently, this passing is > essentially instantaneous. Yes, that is the other possibility (e.g. the behavior of the second example above). Things could certainly be done that. It is obviously debatable, of course, that this alternative is "ideal". > 2) My comment about the local x$9 reemerging as a de facto global (growing > wings as it were and flying the coop) does seem relevant to Fateman's > concern about scope violation and "escaping" variables. Variables like x$9 > are normally off-limits to programmers outside of the subroutine in which > they are declared as local. Does Mathematica rely on the uniqueness of x$n > variable names to keep track internally of multiple versions of global rules > generated programmatically inside a Module, by using them in effect as part > of their "signature"? I know that I can change rules after the fact (i.e., > after the scope of x$n as originally declared in its subroutine has > disappeared) by changing the x$n to which they have been "hard-wired". Of > course, no one who has used Mathematica entertains for a moment the > possibility that local variables are escaping wholesale -- this would make > Mathematica completely unusable, whereas in practice it's eminently usable > (albeit with a steep learning curve). But the observed phenomenon does seem > to be a kind of "back door" and therefore not exactly kosher... This is an observation about an implementational detail, rather than anything fundamental about the language. Values of local variables are essentially always accessible in any programming system. In typical C or Fortran implementations, for example, you can inspect or modify values of arbitrary local variables by using system-dependent commands to determine the corresponding memory locations. The only difference is that, in Mathematica, those variables are more accessible. In Mathematica you can do it by finding the right sym$nnn variable (analogous to finding the right memory location in other systems) and then using ordinary evaluation or assignment (analogous to using memory "peek" and "poke" functions in other systems). So that "back door" exists in any system. A lot of people rather like the accessibility of these private variables, especially for debugging purposes. It would be possible to make them more well hidden in Mathematica by removing them from the symbol table, but I hope that that doesn't happen. There is a fairly clear statement in the documentation not to mess with these variables unless you know what these variables are doing and you have a good reason to mess with them. > PS. I think for clarity it would be best to include a code snippet showing > exactly what I mean here. > Here's a greatly abridged example from actual code: > > \!\(\(\(setRules[sym_: D, \ delayCircleDotEvaluation_: 0]\ := \ > Module[{\ h, \ symL}, \[IndentingNewLine]If[Head[sym]\ =!= \ List, \ > symL\ = \ {sym}, \ > symL\ = \ sym]; \[IndentingNewLine]SetAttributes[ > SmallCircle, {OneIdentity, \ > Flat}]; \[IndentingNewLine]a_\ \[SmallCircle]\ b_\ /; \ > Head[a] === Plus\ || \ Head[b] === Plus := \ > Distribute[h[a, \ b]]\ /. > h -> SmallCircle\ ; \ \[IndentingNewLine]\((a_. \ S_\_u_)\)\ \ > \[SmallCircle]\ \((b_\ \ c_)\)\ := \ > a\ b\ \ S\_u\ \[SmallCircle]\ c\ + \ > a\ S\_u\ \[CircleDot]\ b\ c\ /; \ > MemberQ[symL, \ > S]\ \ && \((FreeAllQ[b, symL]\ || \ > Head[b] === > > CircleDot)\);\[IndentingNewLine]];\)\(\[IndentingNewLine]\ > \) > \)\) > > If you run setRules and then do DownValues[\[SmallCircle], pick out the > rule involving S, and do a FullForm, you will not find S but rather symL$n > instead (its value is in fact that of S, but it is being held; Cases[rule, > symL$n, {0, Infinity}] returns {}). If you change symL$n interactively, > you will change rule accordingly. (But I thought symL$n was local! So this > really is a back door.) Since I don't know what "run setRules" means (evaluate SetRules[] perhaps?) or what "do DownValues[\[SmallCircle]" means (DownValues[\[SmallCircle]] is a syntax error), and I don't know what this function is supposed to do, I can't comment on this example exactly, but I am guessing that the problem is with arguments that are held unevaluated and has nothing to do with variable localization. I am guessing, for example, that Module could be dropped from this example and it still wouldn't do what was intended. That problem can be corrected too, but it is separate issue. > A side observation possibly of interest: If we change the code in setRules > so that S is not first copied to symL (i.e., not "mediated") but used > directly in the rule, the value of S does appear when the rule is examined > using DownValues. This seems to shed some light on Mathematica's timing -- > the value found for S_ is passed to RHS of := very early and is not held but > treated as "raw". I don't understand exactly what this example is either (did you perhaps mean sym used directly, rather than S?), but if that guess is correct then yes, rule application is a way of getting values into held expressions and into scoping contructs like SetDelayed, both of which appear to be needed here. Dave Withoff Wolfram Research