Chapter 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 namespace 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 181 that I have used successfully in large programs. The namespace facility is a more elegant solution 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 that 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. If you are developing library packages that others will use, you should pick a namespace for your procedures and data so that they will not conflict with the applications in which they are used.

Using Namespaces

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. Namespaces can be nested, so you can create a hierarchy of scopes. These concepts are explained in more detail in the rest of this chapter.

One feature not provided by namespaces is any sort of protection, or a way to enforce access controls between different namespaces. This sort of thing is awkward, if not impossible, to provide in a dynamic language like Tcl. For example, you are always free to use namespace eval to reach into any other namespace. Instead of providing strict controls, namespaces are meant to provide structure that enables large scale programming.

The package facility described in Chapter 12 was designed before namespaces. This chapter illustrates a style that ties the two facilities together, but they are not strictly related. It is possible to create a package named A that implements a namespace B, or to use a package without namespaces, or a namespace without a package. However, it makes sense to use the facilities together.

Example 14-1 repeats the random number generator from Example 7-4 on page 91 using namespaces. The standard naming style conventions for namespaces use lowercase:

Example 14-1. Random number generator using namespaces

package provide random 1.0

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 namespace declares what procedures can be imported with the namespace export command. Once you import a procedure, you can use it without a qualified name:

namespace import random::random
random
=> 0.54342849794238679

Importing and exporting are described in more detail later.

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

If a procedure has a variable command that names a new variable, it is created in the namespace when it is first set.

Note

Namespace Variables

Watch out for conflicts with global variables.

You need to be careful when you use variables inside a namespace block. If you declare them with a variable command, they are clearly namespace variables. However, if you forget to declare them, then they will either become namespace variables, or latch onto an existing global variable by the same name. Consider the following code:

namespace eval foo {
    variable table
    for {set i 1} {$i <= 256} {incr i} {
     set table($i) [format %c $i]
    }
}

If there is already a global variable i, then the for loop will use that variable. Otherwise, it will create the foo::i variable. I found this behavior surprising, but it does make it easier to access global variables like env without first declaring them with global inside the namespace block.

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

Note

Qualified Namesqualified namesnamequalified namespace

Declaring variables is more efficient than using qualified names.

The Tcl byte-code compiler generates faster code when you declare namespace and global variables. Each procedure context has its own table of variables. The table can be accessed by a direct slot index, or by a hash table lookup of the variable name. The hash table lookup is slower than the direct slot access. When you use the variable or global command, then the compiler can use a direct slot access. If you use qualified names, the compiler uses the more general hash table lookup.

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 that 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. However, you still need to use namespace eval to create the namespace before you can create procedures inside it. 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:

Example 14-2. 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 have their own specific variable x. The fully qualified names for these variables are ::foo::x, ::bar::x, and ::bar::foo::x.

Example 14-3. Nested namespaces

namespace eval foo {
   variable x 1     ;# ::foo::x
}
namespace eval bar {
   variable x 2     ;# ::bar::x
   namespace eval foo {
      variable x 3  ;# ::bar::foo::x
   }
   puts $foo::x     ;# prints 3
}
puts $foo::x        ;# prints 1

Note

Nested namespaces

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 have two choices. The most general solution is to use the namespace current command to create a fully qualified name:

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

However, it is simpler to just explicitly write out the namespace as in:

trace variable ::myname::x r ::myname::traceproc

The drawback of this approach is that it litters your code with references to ::myname::, which might be subject to change during program development.

Importing and Exporting 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. As a matter of style, I avoid importing names, so I know what package a command belongs to when I'm reading code.

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? ...

Note

Importing and Exporting Procedures

Only exported names appear in package indexes.

When you create the pkgIndex.tcl package index file with pkg_mkIndex, which is described Chapter 12, you should be aware that only exported names appear in the index. Because of this, I often resort to exporting everything. I never plan to import the names, but I do rely on automatic code loading based on the index files. This exports everything:

namespace export *

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

Note

Importing and Exporting Procedures

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.

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 because of the namespace inscope command. In particular, if there is a namespace variable ::current::x, then that variable is modified. An alternative to using namespace code is to name the variable with a qualified name:

set callback {set ::current::x 1}

The drawback of this approach is that it makes it tedious to move the code to a different namespace.

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 131 and 455. 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:

Example 14-4. 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 501 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 190. 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:

Example 14-5. 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 [lsort $result]
}

The namespace Command

Table 14-1 summarizes the namespace operations:

Table 14-1. The namespace command

namespace current

Returns the current namespace.

namespace children ?name? ?pat?

Returns names of nested namespaces. name defaults to current namespace. pat is a string match pattern that limits what is returned.

namespace code script

Generates a namespace inscope command that will eval script in the current namespace.

namespace delete name ?name? ...

Deletes the variables and commands from the specified namespaces.

namespace eval name cmd ?args? ...

Concatenates args, if present, onto cmd and evaluates it in name namespace.

namespace exists name

Returns 1 if namespace name exists, 0 otherwise. (Tcl 8.4)

namespace export ?-clear? ?pat? ?pat? ...

Adds patterns to the export list for current namespace. Returns export list if no patterns.

namespace forget pat ?pat? ...

Undoes the import of names matching patterns.

namespace import ?-force? pat ?pat? ...

Adds the names matching the patterns to the current namespace.

namespace inscope name cmd ?args? ...

Appends args, if present, onto cmd as list elements and evaluates it in name namespace.

namespace origin cmd

Returns the original name of cmd.

namespace parent ?name?

Returns the parent namespace of name, or of the current namespace.

namespace qualifiers name

Returns the part of name up to the last :: in it.

namespace which ?flag? name

Returns the fully qualified version of name. The flag is one of -command, -variable, or -namespace.

namespace tail name

Returns the last component of name.

Converting Existing Packages to use Namespaces

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 eval block. However, you need to consider three things: global variables, exported procedures, and callbacks.

  • Global variables remain global until you change your code to use variable instead of global. Some variables may make sense to leave at the global scope. Remember that the variables that Tcl defines are global, including env, tcl_platform, and the others listed in Table 2-2 on page 31. If you use the upvar #0 trick described on page 92, you can adapt this to namespaces by doing this instead:

    upvar #0 [namespace current]::$instance state
    
  • Exporting procedures makes it more convenient for users of your package. It is not strictly necessary because they can always use qualified names to reference your procedures. An export list is a good hint about which procedures are expected to be used by other packages. Remember that the export list determines what procedures are visible in the index created by pkg_mkIndex.

  • Callbacks execute at the global scope. If you use variable traces and variables associated with Tk widgets, these are also treated as global variables. If you want a callback to invoke a namespace procedure, or if you give out the name of a namespace variable, then you must construct fully qualified variable and procedure names. You can hardwire the current namespace:

    button .foo -command ::myname::callback 
        -textvariable ::myname::textvar
    

    or you can use namespace current:

    button .foo -command [namespace current]::callback 
        -textvariable [namespace current]::textvar
    

[incr Tcl] Object System

The Tcl namespace facility does not 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 that they can implement classes and inheritance. By exploiting these interfaces, various object systems can be added to Tcl as shared libraries.

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. [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. [incr Tcl] From The Ground Up (Chad Smith, Osborn-McGraw Hill, 1999) is an excellent source of information. You can find a version of [incr Tcl] on the CD-ROM. The [incr Tcl] home page is:

The [incr Tcl] sources are maintained on SourceForge:

xotcl Object System

Xotcl is a more recently developed object-oriented extension that blends object-orientation and scripting in a way that preserves the benefits of both. It includes features such as dynamic object aggregation, per-object mixins, filters, dynamic component loading and more. The xotcl home page is:

Notes

The final section of this chapter touches on a variety of features of the namespace facility.

Names for Widgets, Images, and Interpreters

There are a number of Tcl extensions that are not affected by the namespaces described in this chapter, which apply only to commands and variable names. For example, when you create a Tk widget, a Tcl command is also created that corresponds to the Tk widget. This command is always created in the global command namespace even when you create the Tk widget from inside a namespace eval block. Other examples include Tcl interpreters, which are described in Chapter 19, and Tk images, which are described in Chapter 41.

The variable command at the global scope

It turns out that you can use variable like the global command if your procedures are not inside a namespace. This is consistent because it means "this variable belongs to the current namespace," which might be the global namespace.

Auto Loading and auto_import

The following sequence of commands can be used to import commands from the foo package:

package require foo
namespace import foo::*

However, because of the default behavior of packages, there may not be anything that matches foo::* after the package require. Instead, there are entries in the auto_index array that will be used to load those procedures when you first use them. The auto loading mechanism is described in Chapter 12. To account for this, Tcl calls out to a hook procedure called auto_import. This default implementation of this procedure searches auto_index and forcibly loads any pending procedures that match the import pattern. Packages like [incr Tcl] exploit this hook to implement more elaborate schemes. The auto_import hook was first introduced in Tcl 8.0.3.

Namespaces and uplevel

Namespaces affect the Tcl call frames just like procedures do. If you walk the call stack with info level, the namespace frames are visible. This means that 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-5 on page 190 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 significant. 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
namespace eval ::foo::bar { set {} }
=> 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
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.69.152