explicit variable scoping

Jacek Generowicz jacek.generowicz at cern.ch
Tue Mar 23 08:23:03 EST 2004


David MacQuigg <dmq at gain.com> writes:

> When you say "accessing" globals, we need to distinguish between
> "referencing" and "setting".

Actually, I think that "referencing" works fine as it is. The problem
is that "setting" (at the moment) can either be "creating a new
binding" or "rebinding an existing name". Now, we have a way of
disambiguating this in the case of globals (the "global" keyword), but
there is no way of doing it for generally nested scopes. (Remember
that the "globals" keyword was introduced before nested scopes, so
(essentially) all possibilities were covered by it at the time it was
invented.)

But, yes, the problem is that we have one and the same syntax for
binding and re-binding.

> I'm not feeling much need to set variables other than local or
> global,

I think it is important to be able to rebind the currently visible
binding of any name, without having to know or care whether that
binding, is local, nested, or global (or even builtin, maybe)
... while at the same time being able to create a new local binding,
without knowing or caring whether doing so shadows some other binding
to that same name.

All this can be achieved by the simple expedient of distinguishing
binding and re-binding. (Though I have no truly convincing suggestion
of how this distinction should be spelt in Python.)


If anyone cares to see a "gory details" explanation of binding vs
rebinding, read on ... otherwise I recommend you stop here :-)

====================================================================

For example, in the Lisp family of languages, new bindings (new
scopes) are introduced by function parameters and by "let" forms,
while _re_-bindings are performed by some sort of "set" forms.


Here I show the values bound to the names "a", "b" and "c" as the code
shown on the left is being executed.


                        a       b       c
                                
(let ((a "a0")          a0                   New binding of "a"
      (b "b0")          a0      b0           New binding of "b"
      (c "c0"))         a0      b0      c0   New binding of "c"
  (setq a "a1")         a1      b0      c0   Rebinding "a"
  (setq b "b1")         a1      b1      c0   Rebinding "b"
  (setq c "c1")         a1      b1      c1   Rebinding "c"
  (defun foo (a)     Not executed until foo is called 
    (setq a "a2")                   ^
    (setq b "b2")                   |
    (setq c "c2")                   |
    (let ((b "b3"))                 |
      (setq a "a4")                 |
      (setq b "b4")                 |
      (setq c "c4"))                |
    (setq a "a5")                   |
    (setq b "b5")                   V
    (setq c "c5"))   Not executed until foo is called 
  (setq a "a6")         a6       b1      c1  Rebinding "a"
  (setq b "b6")         a6       b6      c1  Rebinding "b"
  (setq c "c6"))        a6       b6      c6  Rebinding "c"



Now, a call to "foo" such as this

(foo "aa")

runs in the lexical environment established above. Let's copy 'n'
paste the source code of foo, and inspect the bindings as the function
is executed. The outer bindings of the names "a", "b" and "c" are in
the state they were left in, at the time "foo" was defined, as shown
above.

Note that now we shadow outer bindings by creating inner bindings to
the same name (in which case two values are listed under a single name
- only the inner binding (the value shown on the right) is visible in
the program).

                        a       b       c
                                      
                        a6      b6      c6  Lexically visible bindings
  (defun foo (a)        a6 aa   b6      c6  Shadow outer "a" with new binding
    (setq a "a2")       a6 a2   b6      c6  Rebind inner "a"
    (setq b "b2")       a6 a2   b2      c6  Rebind outer "b"
    (setq c "c2")       a6 a2   b2      c6  Rebind outer "c"
    (let ((b "b3"))     a6 a2   b2 b3   c6  Shadow outer "b" with new binding
      (setq a "a4")     a6 a4   b2 b3   c6  Rebind inner "a"
      (setq b "b4")     a6 a4   b2 b4   c6  Rebind inner "b"
      (setq c "c4"))    a6 a4   b2 b4   c4  Rebind outer "c"
                        a6 a4   b2      c4  Inner "b" goes out of scope
    (setq a "a5")       a6 a5   b5      c4  Rebind inner "a"
    (setq b "b5")       a6 a5   b5      c4  Rebind "b"
    (setq c "c5"))      a6 a5   b5      c5  Rebind "c"
                        a6      b5      c5  Inner "a" goes out of scope

In Python, both "let" and "setq" are spelt "=". However, in the case
of a global binding "(setq x y)" can be spelt "global x; x = y".

"let" and "setq" give us a way to express whether we want to rebind an
existing binding, or to create a new one.



More information about the Python-list mailing list