[Top] [Prev] [Next] [Bottom]

14

14 Namespaces

Namespaces group procedures and variables into separate name spaces. Namespaces were added in Tcl 8.0. This chapter describes the namespace and variable commands.
Namespaces provide new scopes for procedures and global variables. Originally Tcl had one global scope for shared variables, local scopes within procedures, and one global name space for procedures. The single global scope for procedures and global variables can become unmanageable as your Tcl application grows. I describe some simple naming conventions on page 143 that I have used successfully in large programs. However, Tcl 8.0 adds a more elegant namespace facility that partitions the global scope for procedure names and global variables.

Namespaces help structure large Tcl applications, but they add complexity. In particular, command callbacks may have to be handled specially so they execute in the proper namespace. You choose whether or not you need the extra structure and learning curve of namespaces. If your applications are small, then you can ignore the namespace facility.

Using Namespaces

Namespaces can be nested, so you can create a hierarchy of scopes. Namespaces add new syntax to procedure and variable names. A double-colon, ::, separates the namespace name from the variable or procedure name. You use this syntax to reference procedures and variables in a different namespace. The namespace import command lets you name things in other namespaces without the extra syntax. These concepts are explained in more detail in the rest of this chapter.

Example 14-1 repeats the random number generator from Example 7-4 on page 78 using namespaces:

Random number generator using namespaces.
namespace eval Random {
	# Create a variable inside the namespace
	variable seed [clock seconds]

	# Make the procedures visible to namespace import
	namespace export Init Random Range

	# Create procedures inside the namespace
	proc Init { value } {
		variable seed
		set seed $value
	}
	proc Random {} {
		variable seed
		set seed [expr ($seed*9301 + 49297) % 233280]
		return [expr $seed/double(233280)]
	}
	proc Range { range } {
		expr int([Random]*$range)	
	}
}
Example 14-1 defines three procedures and a variable inside the namespace Random. From inside the namespace you can use these procedures and variables directly. From outside the namespace you use the :: syntax for namespace qualifiers. For example, the state variable is just seed within the namespace, but you use Random::seed to refer to the variable from outside the namespace. Using the procedures looks like this:

Random::Random
=> 0.3993355624142661

Random::Range 10
=> 4

If you use a package a lot you can import its procedures. A package declares what procedures can be imported with the namespace export command. Importing and exporting are described in more detail later. Once you import a procedure you can use it without a qualified name:

namespace import Random::Random
Random
=> 0.54342849794238679

Namespace Variables

The variable command defines a variable inside a namespace. It is like the set command because it can define a value for the variable. You can declare several namespace variables with one variable command. The general form is:

variable name ?value? ?name value? ...
If you have an array, do not assign a value in the variable command. Instead, use regular Tcl commands after you declare the variable. You can put any commands inside a namespace block:

namespace eval foo {
	variable arr
	array set arr {name value name2 value2}
}
A namespace variable is similar to a global variable because it is outside the scope of any procedures. Procedures use the variable command or qualified names to reference namespace variables. For example, the Random procedure has a variable command that brings the namespace variable into the current scope:

variable seed
You do not have to have a variable command inside the namespace block. It is only useful there to give the namespace variable an initial value. If a procedure has a variable command that names a new variable, it is created in the namespace when it is first set.

Qualified Names

A fully qualified name begins with ::, which is the name for the global namespace. A fully qualified name unambiguously names a procedure or a variable. The fully qualified name works anywhere. If you use a fully qualified variable name, it is not necessary to use a global command. For example, suppose namespace foo has a namespace variable x, and there is also a global variable x. The global variable x can be named with this:

::x
The :: syntax does not affect variable substitutions. You can get the value of the global variable x with $::x. Name the namespace variable x with this:

::foo::x
A partially qualified name does not have a leading ::. In this case the name is resolved from the current namespace. For example, the following also names the namespace variable x:

foo::x
You can use qualified names with global. Once you do this, you can access the variable with its short name:

global ::foo::x
set x 5

Command Lookup

A command is looked up first in the current name space. If it is not found there, then it is looked up in the global namespace. This means you can use all the built-in Tcl commands inside a namespace with no special effort.

You can play games by redefining commands within a namespace. For example, a namespace could define a procedure named set. To get the built-in set you could use ::set, while set referred to the set defined inside namespace. Obviously you need to be quite careful when you do this.

You can use qualified names when defining procedures. This eliminates the need to put the proc commands inside a namespace block. Example 14-2 repeats the random number generator using qualified names. Random::Init does not need a variable command because it uses a qualified name for seed:

Random number generator using qualified names.
namespace eval Random {
	# Create a variable inside the namespace
	variable seed [clock seconds]
}
# Create procedures inside the namespace
proc Random::Init { seed } {
	set ::Random::seed $seed
}
proc Random::Random {} {
	variable seed
	set seed [expr ($seed*9301 + 49297) % 233280]
	return [expr $seed/double(233280)]
}
proc Random::Range { range } {
	expr int([Random]*$range)	
}

Nested Namespaces

Namespaces can be nested inside other namespaces. Example 14-3 shows three namespaces that each have their own variable x. The fully qualified names for these variables are ::foo::x, ::bar::x, and ::bar::foo::x.

Nested namespaces.
namespace eval foo {
	variable x 1					;# ::foo::x
}
namespace eval bar {
	variable x 2					;# ::bar::x
	namespace foo {
		variable x 3				;# ::bar::foo::x
	}
	puts $foo::x					;# prints 3
}
puts $foo::x						;# prints 1
Partially qualified names can refer to two different objects.

In Example 14-3 the partially qualified name foo::x can reference one of two variables depending on the current namespace. From the global scope the name foo::x refers to the namespace variable x inside ::foo. From the ::bar namespace, foo::x refers to the variable x inside ::bar::foo.

If you want to unambiguously name a variable in the current namespace, you have two choices. The simplest is to bring the variable into scope with the variable command:

variable x
set x something
If you need to give out the name of the variable, then you can use the namespace current command to create a fully qualified name:

trace variable [namespace current]::x r \
	[namespace current]::traceproc

Importing Procedures

Commands can be imported from namespaces to make it easier to name them. An imported command can be used without its namespace qualifier. Each namespace specifies exported procedures that can be the target of an import. Variables cannot be imported. Note that importing is only a convenience; you can always use qualified names to access any procedure.

The namespace export command goes inside the namespace block, and it specifies what procedures a namespace exports. The specification is a list of string match patterns that are compared against the set of commands defined in a namespace. The export list can be defined before the procedures being exported. You can do more than one namespace export to add more procedures, or patterns, to the export list for a namespace. Use the -clear flag if you need to reset the export list.

namespace export ?-clear? ?pat? ?pat? ...
The namespace import command makes commands in another namespace visible in the current namespace. An import can cause conflicts with commands in the current namespace. The namespace import command raises an error if there is a conflict. You can override this with the -force option. The general form of the command is:

namespace import ?-force? namespace::pat ?namespace::pat?...
The pat is a string match type pattern that is matched against exported commands defined in namespace. You cannot use patterns to match namespace. The namespace can be a fully or partially qualified name of a namespace.

If you are lazy, you can import all procedures from a namespace:

namespace import Random::*
The drawback of this approach is that Random exports an Init procedure, which might conflict with another module you import in the same way. It is safer to import just the procedures you plan on using:

namespace import Random::Random Random::Range
A namespace import takes a snapshot.

If the set of procedures in a namespace changes, or if its export list changes, then this has no effect on any imports that have already occurred from that namespace.

Other Namespaces

Several subsystems have their own namespaces for their objects. These other namespaces are independent of the procedure and variable namespaces. Named objects include Tcl interpreters, which are described on page 205, Tk widgets, which are introduced on page 228, and Tk images, which are introduced on page 458. For each instance of the object, a Tcl command is created with the same name as the object. These are always global command names, even if the code that creates the object is in a namespace. You can use rename to move one of these global commands into a namespace, but the name of the object does not change.

Callbacks and Namespaces

Commands like after, bind, and button take arguments that are Tcl scripts that are evaluated later. These callback commands execute later in the global scope by default. If you want a callback to be evaluated in a particular namespace, you can construct the callback with namespace code. This command does not execute the callback. Instead, it generates a Tcl command that will execute in the current namespace scope when it is evaluated later. For example, suppose ::current is the current namespace. The namespace code command determines the current scope and adds that to the namespace inscope command it generates:

set callback [namespace code {set x 1}]
=> namespace inscope ::current {set x 1}

# sometime later ...
eval $callback
When you evaluate $callback later, it executes in the ::current namespace. In particular, if there is a namespace variable ::current::x, then that variable is modified. Compare this without namespaces:

set callback {set x 1}
# sometime later ...
eval $callback
If you need substitutions to occur on the command when you define it, use list to construct it. Using list is discussed in more detail on pages 114 and 305. Example 14-4 wraps up the list and the namespace inscope into the code procedure, which is handy because you almost always want to use list when constructing callbacks. The uplevel in code ensures that the correct namespace is captured; you can use code anywhere:

The code procedure to wrap callbacks.
proc code {args} {
	set namespace [uplevel {namespace current}]
	return [list namespace inscope $namespace $args]
}
namespace eval foo {
	variable y "y value" x {}
	set callback [code set x $y]
	=> namespace inscope ::foo {set x {y value}}
}
The example defines a callback that will set ::foo::x to y value. If you want to set x to the value that y has at the time of the callback, then you do not want to do any substitutions. In that case, the original namespace code is what you want:

set callback [namespace code {set x $y}]
=> namespace inscope ::foo {set x $y}

If the callback has additional arguments added by the caller, namespace inscope correctly adds them. For example, the scrollbar protocol described on page 347 adds parameters to the callback that controls a scrollbar.

Introspection

The info commands operation returns all the commands that are currently visible. It is described in more detail on page 150. You can limit the information returned with a string match pattern. You can also include a namespace specifier in the pattern to see what is visible in a namespace. Remember that global commands and imported commands are visible, so info commands returns more than just what is defined by the namespace. Example 14-5 uses namespace origin, which returns the original name of imported commands, to sort out the commands that are really defined in a namespace:

Listing commands defined by a namespace.
proc Namespace_List {{namespace {}}} {
	if {[string length $namespace] == 0} {
		# Determine the namespace of our caller
		set namespace [uplevel {namespace current}]
	}
	set result {}
	foreach cmd [info commands ${namespace}::*] {
		if {[namespace origin $cmd] == $cmd} {
			lappend result $cmd
		}
	}
	return $result
}

The namespace Command

Table 14-1 summarizes the namespace operations:
The namespace command.
namespace current Return the current namespace.
namespace children ?name? ?pat? Return names of nested namespaces. name defaults to current namespace. pat is a string match pattern that limits what is returned.
namespace code script Generate a namespace inscope command that will eval script in the current namespace.
namespace delete name ?name? ... Delete the variables and commands from the specified namespaces.
namespace eval name cmd ?args? ... Concatenate args, if present, onto cmd and evaluate it in name namespace.
namespace export ?-clear? ?pat? ?pat? ... Add patterns to the export list for current namespace. Returns export list if no patterns.
namespace forget pat ?pat? ... Undo the import of names matching patterns.
namespace import ?-force? pat ?pat? ... Add the names matching the patterns to the current namespace.
namespace inscope name cmd ?args? ... Append args, if present, onto cmd as list elements and evaluate it in name namespace.
namespace origin cmd Return the original name of cmd.
namespace parent ?name? Return the parent namespace of name., or of the current namespace.
namespace qualifiers name Return the part of name up to the last :: in it.
namespace which ?flag? name Return the fully qualified version of name. The flag is one of -command, -variable, or -namespace.
namespace tail name Return the last component of name.

Wrapping Existing Packages

Suppose you have an existing set of Tcl procedures that you want to wrap in a namespace. Obviously, you start by surrounding your existing code in a namespace block. However, you need to consider three things: global variables, exported procedures, and callbacks.

upvar #0 [namespace current]::$instance state
button .foo -command [namespace current]::callback \
	-textvariable [namespace current]::textvar

[incr Tcl] Object System

The Tcl namespace facility was proposed by Michael Mclennan based on his experiences with [incr Tcl], which is the most widely used object-oriented extension for Tcl. The Tcl namespace facility does not directly provide classes and inheritance. It just provides new scopes and a way to hide procedures and variables inside a scope. There are Tcl C APIs that support hooks in variable name and command lookup for object systems so they can implement classes and inheritence. The goal is that various object systems can be added to Tcl as shared libraries.

[incr Tcl] provides classes, inheritance, and protected variables and commands. If you are familiar with C++, [incr Tcl] should feel similar. A complete treatment of [incr Tcl] is not made in this book. Tcl/Tk Tools ( Mark Harrison, O'Reilly & Associates, Inc., 1997) is an excellent source of information. You can find a version of [incr Tcl] on the CD-ROM. The [incr Tcl] home page is http://www.tcltk.com/itcl/.

Notes

The namespace facility is brand new. I have participated in the design, but I do not have much experience using it, yet. This book goes to press slightly before the final release of Tcl 8.0. It is possible that last minute changes to namespaces will result in corrections to this book at a later time. You can always consult my on-line errata at http://www.beedub.com/book/.

Auto Loading

Currently there is no special interaction between namespace import and the auto loading mechanism described in Chapter 12. Make sure the target package is loaded before doing namespace import.

Namespaces and uplevel

Namespaces affect the Tcl call frames just like procedures. If you walk the call stack with info level, the namespace frames are visible. This means you can get access to all variables with uplevel and upvar. Level #0 is still the absolute global scope, outside any namespace or procedure. Try out Call_Trace from Example 13-4 on page 151 on your code that uses namespaces to see the effect.

Naming Quirks

When you name a namespace, you are allowed to have extra colons at the end. You can also have two or more colons as the separator between namespace name components. These rules make it easier to assemble names by adding to the value returned from namespace current. These all name the same namespace:

::foo::bar
::foo::bar::
::foo::::bar
The name of the global namespace can be either :: or the empty string. This follows from the treatment of :: in namespace names.

When you name a variable or command, a trailing :: is treated differently. In the following command a variable inside the ::foo::bar namespace is modified. The variable has an empty string for its name:

set ::foo::bar:: 3
If you want to embed a reference to a variable just before two colons, use a backslash to turn off the variable name parsing before the colons:

set x xval
set y $x\::foo
=> xval::foo

Miscellaneous

You can remove names you have imported:

namespace forget Random::Init
You can rename imported procedures to modify their names:

rename Range range
You can even move a procedure into another namespace with rename:

rename Random::Init myspace::Init


[Top] [Prev] [Next] [Bottom]

[email protected]
Copyright © 1997, Brent Welch. All rights reserved.
This will be published by Prentice Hall as the 2nd Edition of
Practical Programming in Tcl and Tk