TIP 52: Hierarchical Namespace Lookup of Commands and Variables

Login
Author:         David Cuthbert <[email protected]>
Author:         Andreas Kupries <[email protected]>
State:          Withdrawn
Type:           Project
Vote:           Pending
Created:        09-Aug-2001
Post-History:   
Discussions-To: news:comp.lang.tcl
Keywords:       namespace,lookup,hierarchy
Tcl-Version:    8.5

Abstract

This TIP proposes to change the command and variable namespace lookup system so that the full hierarchy of namespaces is parsed, rather than just the current namespace followed by the global namespace. This is primarily intended to rectify problems often encountered with the use of [incr Tcl] (ITcl) and namespaces. In addition, package encapsulation can be enhanced with judicious application of this feature.

Rationale

Currently, the following code is invalid in Tcl/ITcl:

package require Itcl

namespace eval SampleNS {
    proc Hello {} { puts "Hello world!" }

    ::itcl::class X {
        public constructor {} {} { Hello }
    }
}

SampleNS::X x1  ;# Error: invalid command name "Hello"

This is due to the fact that ITcl classes double as namespaces. Therefore, the lookup of Hello takes place first in ::SampleNS::X, followed by :: (the global namespace).

The current workaround - to reopen the class' namespace and issue a namespace import directive - is of limited value since namespace import is not capable of bringing in names defined later on. The following code illustrates this point:

package require Itcl

namespace eval SampleNS {
    ::itcl::class X1 {
        public method GetSibling {} { return [X2 \#auto] }
    }
    namespace eval X1 { namespace import ::SampleNS }

    # Further down, or perhaps in a separate file source later:

    ::itcl::class X2 { }
}

set x [SampleNS::X1 \#auto]
$x GetSibling ;# Error: invalid command name "X2"

Non-ITcl code can also make use of hierarchical namespaces to better encapsulate support procedures. In this example, the child namespace private illustrates that the GetUniqueId procedure should not be used outside of the package; however, GetUniqueId still has access to the procedures and variables in the package's main namespace:

# MyPackage

namespace eval MyPackage {
    variable nextId 0

    namespace eval private {
        proc GetUniqueId {} {
            variable nextId
            return "MyPackage.[incr nextId]"
        }
    }

    proc CreateObject {} {
        set name ::[private::GetUniqueId]
        proc $name args { body }
        return $name
    }
}

Specification

Currently, the NAME RESOLUTION section of the namespace documentation states:

If the name does not start with a :: (i.e., is relative), Tcl follows a fixed rule for looking it up: Command and variable names are always resolved by looking first in the current namespace, and then in the global namespace. Namespace names, on the other hand, are always resolved by looking in only the current namespace.

The proposed change to this is as follows:

If the name does not start with a :: (i.e., is relative), Tcl follows a fixed rule for looking it up: Command and variable names are always resolved by traversing the namespace hierarchy - that is, the current namespace is examined first, followed by the parent, the parent's parent, and so on, until (finally) the global namespace is examined. Namespace names, on the other hand, are always resolved by looking in only the current namespace.

By keeping the current behaviour for namespace names, this TIP affects only completely unqualified commands and variables (i.e. those that do not contain ::). Changing the behaviour of partially qualified names (those that are relative and contain ::) is often unintuitive and can lead to unexpected errors.

Consequences

  1. ITcl classes and child namespaces can refer to command and variable names in their parent hierarchy without requiring the names to be fully qualified. This improves the intuitiveness and readability of Tcl code. In addition, it can reduce the brittleness of the code should parent namespace names undergo a change (e.g., namespace eval scriptics.com to namespace eval ajubasolutions.com).

  2. Currently well-defined behaviour is modified. This can break existing code if the following conditions are met:

    * The code employs the use of namespaces with a depth greater than one below the global namespace.

    * The code creates a variable or procedure in a parent namespace with the same name as a variable or procedure in the global namespace.

    * The code in the child namespace uses unscoped names to refer to commands and/or variables in the global namespace.

    A cursory examination of existing Tcl code available on the Internet revealed no code which used deeply nested namespaces.

  3. Existing well-defined behaviour of the internal Tcl function TclGetNamespaceForQualName is modified. Under the sample implementation, the altNsPtrPtr parameter (which currently returns a pointer to the global namespace if a name was found there) always returns NULL. It is up to the calling functions (e.g., Tcl_FindCommand and Tcl_FindNamespaceVar) to traverse the hierarchy. Although the Tcl and Tk code-base can be modified to accommodate this, extensions which depend on this internal function may be broken.

Namespace History

Namespaces were originally developed by Michael McLennan for ITcl, and apparently had this hierarchical resolution feature. When they were adopted into Tcl, an optimisation was made which led to the current behaviour.

This TIP argues for the reversal of this decision based on experiences with the new behaviour.

See Also

Comments

Notice of Withdrawal

This TIP was Withdrawn by the TIP Editor following discussion on the tcl-core mailing list. The following is a summary of reasons for withdrawal:

Insufficiently subtle. 52 will break any code that assumes the current behaviour (and you can bet someone will have that assumption) and 142 doesn't let two namespaces have different search paths (unless the variable is always interpreted locally, which just creates bizarre variable name magic.)

Copyright

Copyright © 2001 by David Cuthbert. Distribution in whole or part, with or without annotations, is unlimited.