TIP #52: HIERARCHICAL NAMESPACE LOOKUP OF COMMANDS AND VARIABLES ================================================================== Version: $Revision: 1.6 $ Author: David Cuthbert Andreas Kupries State: Withdrawn Type: Project Tcl-Version: 8.5 Vote: Pending Created: Thursday, 09 August 2001 URL: https://tip.tcl-lang.org52.html Discussions-To: news:comp.lang.tcl Post-History: ------------------------------------------------------------------------- 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 ========== * Tcl manual page /namespace/. * Tcl source code file /tcl8.4a3/generic/tclNamesp.c/. * Sample implementation at COMMENTS ========== * Andreas Kupries: Related information: SF entry [ #218101 ] "no man page for library procedures Tcl_AddInterpResolver Tcl" [] Not addressed in this TIP: Impact on speed of the interpreter. (Seeking out mail on Tcl core where author of talks about this) 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. ------------------------------------------------------------------------- TIP AutoGenerator - written by Donal K. Fellows