[Python-ideas] Jump to function as an an alternative to call function
Stephen J. Turnbull
turnbull.stephen.fw at u.tsukuba.ac.jp
Thu Aug 16 14:00:21 EDT 2018
tl;dr A pedantic note on dynamic binding.
Steven D'Aprano writes:
> The most common language today that uses dynamic scoping is probably
> Emacs Lisp.
AFAIK all Common-Lisp-ish languages implement dynamic scoping:
anything defvar'd is dynamically-scoped.
> Although dynamic scoping is very powerful, it is also harder
> to implement correctly (so I believe)
In fact, lexical scoping is harder to do correctly because you need to
know what you're doing (determine whether a name is in the lexical
scope). (Old) Emacs Lisp-style dynamic scoping (everything is
dynamically scoped) is (conceptually) easy to implement correctly (but
inefficiently!) simply by using a stack of bindings and doing all
accesses by searching that stack top to bottom for a relevant binding.
It's a pain in the butt to use *correctly* because every such binding
can affect anything else in your program and any libraries you import.
That's why Common Lisp and modern Emacs Lisp are lexically-scoped.[1]
It's not that much harder to implement the dynamic scoping of defvars;
you just have to add a check for the symbol being a defvar at binding
time, and if not put it in the local namespace.
It's true (as you write later in the thread) that Richard Stallman is
still enamoured of dynamic scoping. The nicest thing I can say about
Richard is that he has enormous capacity to hold huge complex
structures in his head. The next nicest thing I can say about Richard
is that he's an anomoly. If his enthusiasm for dynamic scoping is
correct, it's a "stopped clock right twice a day" phenomenon. What's
good for Richard may be *convenient* for the rest of us when hacking
up one-offs, but I firmly believe that if *we* want to write reliable,
nearly correct software for complex systems, dynamic scoping should be
used sparingly at most.
After 2 decades of maintaining Emacsen, I'd go so far as to say that
Python's "global" and "nonlocal" declarations are distinctly
preferable to Lisp's "defvar" and "special" declarations where
Python's much more restricted constructs can do the job.
> It is generally considered that dynamic scoping is equivalent to
> the exclusive use of global variables
That depends on your definition of "equivalent" and "global". At
least psychologically, they can be very different regardless of the
definition of "variable". In (old) Emacs Lisp, defun, lambda, and let
all appear to define local variables, but they don't. They define
local bindings of global variables, which affect any function called
recursively from the function that does the binding that uses those
variables as globals.
And in any Lisp, it is in principle[2] impossible to write a correct
program using dynamic scope if you import another module, because even
if you've checked that module and there are no free occurrances of a
given name at write-time, the module could be updated to use a free
variable by that name before you run it and you will shadow its
expected binding at run-time. To be safe, you need to implement
lexical scope! In languages where "global" names are bound to memory
locations at compile time, like C, or in Python where "global" means
module scope, you don't have this problem. Sure, in C you can declare
a variable "extern" or in Python you can import a name from another
module, but that solves the problem: without an *explicit* declaration
of that kind you know you can't affect another module.
The point, of course, is not so much the "in principle" hazard, but
rather the need to determine what the scope of a given binding *could*
be and to check large extents of source code for bugs due to
unexpected shadow bindings of free variables in order to be sure your
program is correct, and that there is no restriction in principle on
how large that extent might be.
Steve
Footnotes:
[1] Technically, I think Emacs still defaults to dynamic scope and
you have to turn lexical scope on with a pragma.
[2] Again, technically, you can use dynamic scope safely in Common
Lisp by interning your dynamic variables in a package, and so
restricting their scope to your module plus any other modules that
deliberate import those names.
More information about the Python-ideas
mailing list