[Python-Dev] statically nested scopes

Jeremy Hylton jeremy@alum.mit.edu
Wed, 1 Nov 2000 14:07:10 -0500 (EST)


Title: Statically Nested Scopes
Author: Jeremy Hylton <jeremy@digicool.com>
Status: Draft
Type: Standards Track
Created: 01-Nov-2000

Abstract

    This PEP proposes the additional of statically nested scoping
    (lexical scoping) for Python 2.1.  The current language definition
    defines exactly three namespaces that are used to resolve names --
    the local, global, and built-in namespaces.  The addition of
    nested scopes would allow resolution of unbound local names in
    enclosing functions' namespaces.

    One consequence of this change that will be most visible to Python
    programs is that lambda statements could reference variables in
    the namespaces where the lambda is defined.  Currently, a lambda
    statement uses default arguments to explicitly creating bindings
    in the lambda's namespace.

Notes

    This section describes several issues that will be fleshed out and
    addressed in the final draft of the PEP.  Until that draft is
    ready, please direct comments to the author.

    This change has been proposed many times in the past.  It has
    always been stymied by the possibility of creating cycles that
    could not be collected by Python's reference counting garbage
    collector.  The additional of the cycle collector in Python 2.0
    eliminates this concern.

    Guido once explained that his original reservation about nested
    scopes was a reaction to their overuse in Pascal.  In large Pascal
    programs he was familiar with, block structure was overused as an
    organizing principle for the program, leading to hard-to-read
    code. 

    Greg Ewing developed a proposal "Python Nested Lexical Scoping
    Enhancement" in Aug. 1999.  It is available from
    http://www.cosc.canterbury.ac.nz/~greg/python/lexscope.html

    Michael Hudson's bytecodehacks projects at
    http://sourceforge.net/projects/bytecodehacks/
    provides facilities to support nested scopes using the closure
    module. 

    Examples:

    def make_adder(n):
        def adder(x):
            return x + n
        return adder
    add2 = make_adder(2)
    add2(5) == 7

    
    from Tkinter import *
    root = Tk()
    Button(root, text="Click here",
           command = lambda : root.test.configure(text="..."))

    
    One controversial issue is whether it should be possible to modify
    the value of variables defined in an enclosing scope.  

    One part of the issue is how to specify that an assignment in the
    local scope should reference to the definition of the variable in
    an enclosing scope.  Assignment to a variable in the current scope
    creates a local variable in the scope.  If the assignment is
    supposed to refer to a global variable, the global statement must
    be used to prevent a local name from being created.  Presumably,
    another keyword would be required to specify "nearest enclosing
    scope."

    Guido is opposed to allow modifications (need to clarify exactly
    why).  If you are modifying variables bound in enclosing scopes,
    you should be using a class, he says.

    The problem occurs only when a program attempts to rebind the name
    in the enclosing scope.  A mutable object, e.g. a list or
    dictionary, can be modified by a reference in a nested scope; this
    is an obvious consequence of Python's reference semantics.  The
    ability to change mutable objects leads to an inelegant
    workaround: If a program needs to rebind an immutable object,
    e.g. a number or tuple, store the object in a list and have all
    references to the object use this list:

    def bank_account(initial_balance):
        balance = [initial_balance]
        def deposit(amount):
            balance[0] = balance[0] + amount
        def withdraw(amount):
            balance[0] = balance[0] - amount
        return deposit, withdraw

    I would prefer for the language to support this style of
    programming directly rather than encouraging programs to use this
    somewhat obfuscated style.  Of course, an instance would probably
    be clearer in this case.
 
    One implementation issue is how to represent the environment that
    stores variables that are referenced by nested scopes.  One
    possibility is to add a pointer to each frame's statically
    enclosing frame and walk the chain of links each time a non-local
    variable is accessed.  This implementation has some problems,
    because access to nonlocal variables is slow and causes garbage to
    accumulate unncessarily.  Another possibility is to construct an
    environment for each function that provides access to only the
    non-local variables.  This environment would be explicitly passed
    to nested functions.