[Python-Dev] Re: Dynamic nested scopes

Tim Peters tim_one@email.msn.com
Thu, 2 Nov 2000 12:43:03 -0500


[Jeremy Hylton]
> ...
> There are few languages that use dynamic scoping for normal name
> resolution.  Many early Lisp implementations did, but I think all the
> modern ones use lexical scoping instead.

I believe all early Lisps were dynamically scoped.  Scheme changed that, and
Common Lisp followed.  Many interpreted languages *start* life with dynamic
scoping because it's easy to hack together, but that never lasts.  REBOL
went thru this a couple years ago, switching entirely from dynamic to
lexical before its first public release, and breaking most existing programs
in the process.  Perl also started with dynamic scoping, but, in Perl-like
fashion, Perl5 *added* lexical scoping on top of dynamic ("local" vars use
dynamic scoping; "my" vars lexical; and all Perl5 gotcha guides stridently
recommend never using "local" anymore).  Here's some Perl5:

$i = 1;

sub f {
    local($i) = 2;
    &g();
}

sub g {
    return $i;
}

print "i at start is $i\n";
print "i in g called directly is ", &g(), "\n";
print "i in g called indirectly via f is ", &f(), "\n";
print "i at end is $i\n";

Here's what it prints:

i at start is 1
i in g called directly is 1
i in g called indirectly via f is 2
i at end is 1

> It is hard to write modular code using dynamic scope, because the
> behavior of a function with free variables can not be determined by
> the module that defines it.

As shown above; dynamic scoping is a nightmare even in the absence of nested
functions.

> Not saying it isn't useful, just that it makes it much harder to reason
> about how a particular modular or function works in isolation from the
> rest of the system.

People who spend too much time writing meta-systems <wink> overestimate its
usefulness.  Most programmers who need this kind of effect would be much
better off explicitly passing a dict of name->value mappings explicitly
manipulated, or simply passing the values of interest (if I want a function
that sucks the value of "i" out of its caller, what clearer way than for the
caller to pass i in the arglist?!  dynamic scoping is much subtler, of
course -- it sucks the value of "i" out of *whatever* function up the call
chain happened to define an i "most recently").

Lexical scoping is much tamer, and, indeed, Python is one of the few modern
languages that doesn't support it.  Last time Guido and I hand-wrung over
this, that was the chief reason to implement it:  newcomers are endlessly
surprised that when they write functions that visually nest, their scopes
nevertheless don't nest.  There's certainly nothing novel or unexpected
about lexical scoping anymore.

The problem unique to Python is its rule for determining which vrbls are
local; in Lisps, REBOL and Perl, you have to explicitly name all vrbls local
to a given scope, which renders "how do you rebind a non-local name?" a
non-issue.

has-the-feel-of-inevitability-ly y'rs  - tim