TIP #52: Hierarchical Namespace Lookup of Commands and Variables


TIP:52
Title:Hierarchical Namespace Lookup of Commands and Variables
Version:$Revision: 1.6 $
Authors: David Cuthbert <dacut at kanga dot org>
Andreas Kupries <andreas_kupries at users dot sourceforge dot net>
State:Withdrawn
Type:Project
Tcl-Version:8.5
Vote:Pending
Created:Thursday, 09 August 2001
Discussions To:news:comp.lang.tcl
Keywords:namespace, lookup, hierarchy

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:

  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.


Powered by Tcl[Index] [History] [HTML Format] [Source Format] [LaTeX Format] [Text Format] [XML Format] [*roff Format (experimental)] [RTF Format (experimental)]

TIP AutoGenerator - written by Donal K. Fellows