[Python-Dev] Re: Dynamic nested scopes

Ken Manheimer klm@digicool.com
Tue, 14 Nov 2000 10:53:47 -0500 (EST)


On Thu, 2 Nov 2000, Jeremy Hylton wrote:

> 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.  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.  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.

There's a seminal steele and sussman paper, _The Art of the Interpreter,
or the Modularity Complex (Parts Zero, One, and Two)_, which, among other
things, introduces closures and describes why dynamic scoping is evil.  
(The paper sketches out an elementary, lexically scoped lisp interpreter
which i think is the precursor to scheme.)  It's been over 20 years since
i read it, but i recall something known as the funarg problem, where
dynamic scoping + recursion (maybe, + functions as first class objects)
inherently breaks certain parameter passing arrangements.

If you're interested, see:

  ftp://publications.ai.mit.edu/ai-publications/0-499/AIM-453.ps

(I'm working at home today, so can't even glance at it to refresh my
memory, and oughtn't at the moment, anyway.)

In any case, dynamic scoping breaks locality of reference for just about
any definition of "locality".

With lexical scoping, you can trace through the text of the program to
track down implicit references.  Even with acquisition, the containment
connections are made explicitly (though it seems magic with web
programming, because the zope publisher applies .__of__() for you), and
can be nailed down by introspection on the objects (.aq_base, .aq_parent,
.aq_inner...).

With dynamic scoping, variable references go up the stack to the context
of *any* intervening call frame, with no way for the programmer to know
where it's being resolved.  In practice, even without the funarg breakage,
it doesn't scale up - it's too promiscuous a state-communication
mechanism.  You can work around it - a lot of emacs lisp code does so -
but it's a terrible burden, not a benefit.

Ken