Tcl Namespaces

Namespaces provide a means of organizing procedures and variables in Tcl.

A namespace basically creates a unique local scope. Within each unique scope, variables and procedures created in that scope are isolated from both the global scope and from other scopes. This allows the developer a means to segregate procedures and variables in an organized manner. Namespaces can be nested, and procedures and variables can be imported to, exported from, and referenced in other namespaces or the global scope. The following diagram shows a visual representation of the global scope and several different namespaces, including variables and procedures defined in each.


Figure 1.

A global procedure acts just like any function within Tcl. A global procedure can be called from a namespace without any need to import or otherwise localize the procedure. A procedure within a namespace is local to the namespace unless it is explicitly called or the procedure is exported from the namespace. The global scope is also designed such that any variables defined outside of a procedure or a namespace are automatically in the global scope. To access a global variable in a local scope, such as a procedure or namespace, you will need to use the global command. As a script grows and as the number of scripts running simultaneously increases, the ability of the developer to avoid conflict between the names of procedures and variables defined in the global scope decreases.

The following Tcl code demonstrates how the global command permits access to global variables in a local scope.
set f "This is a global variable";

#This procedure cannot access the global variable f.
proc testproc1 { } {
   catch {puts $f} val;
   puts $val;
}

#This procedure can access the global variable f using the global command.
proc testproc2 { } {
   global f;
   catch {puts $f} val;
   puts $val;
}
testproc1;
can't read "f": no such variable

testproc2;
This is a global variable

Likewise, any variable declared as global from within the procedure where the variable is defined is also included in the global scope, once the procedure has been run. Any attempt to access the variable before running the procedure will cause an error. The upvar command can be used to change the availability of a variable by providing a pointer to the source variable’s contents.

Namespaces add another level of syntax to procedure and variable names. A double colon :: is the separator between namespace names and procedure/variable names. An example of a namespace called my_namespace would be ::my_namespace. The global namespace indicator is just the double colon. This means that instead of using the global command, it is also possible to reference global variables inside procedures using the global namespace indicator.
set ::element_list "1 2 3 4 5";
proc ::element_info {} {
   puts $::element_list;
   set ::node_list "10 20 30 40";
   puts $::node_list;
}
element_info;
1 2 3 4 5
10 20 30 40

puts $::element_list;
1 2 3 4 5

puts $::node_list;
10 20 30 40
However, using global variables is risky because they may conflict with variables or procedures defined in the global scope of other Tcl scripts. Instead, user-defined namespaces are recommended. The following table contains some commonly used namespace commands and a summary of their usage. For more in-depth explanations and a complete listing, referred to http://www.tcl.tk/man/ or to a Tcl/Tk handbook.
namespace eval name {body}
Used to define a namespace or to run commands inside a namespace.
namespace current
Returns the qualified name of the current namespace.
namespace delete name
Deletes and existing namespace.
namespace exists name
Check for the existence of a namespace.
variable name ?value?
Used to define, and optionally assign a value to, a variable within a namespace.
The variable command can be used to share variables between procedures and the main body of the Tcl script.
namespace eval ::my_namespace {
   variable element_list "1 2 3 4 5";
}

proc ::my_namespace::element_info {} {
   variable element_list;
   puts $element_list;

   variable node_list "10 20 30 40";
   puts $::node_list;
}

::my_namespace::element_info;
1 2 3 4 5
10 20 30 40

puts $::my_namespace::element_list;
1 2 3 4 5

puts $::my_namespace::node_list;
10 20 30 40
The following code gives examples of how variables and procedures can be manipulated from namespaces, procedures, and the global scope.
#Script begins.

#Define a variable in the global scope.
set f "This is a global variable";

#This global procedure will generate an error because the
#global variable f is not in the scope.
proc testproc1 {} {
   catch {puts $f} g;
   puts $g;
}

#This procedure will not generate an error because the
#global variable f is localized with the global command.
proc testproc2 {} {
   global f;
   catch {puts $f} g;
   puts $g;
}

#A call to delete the namespace clears variables within
#that namespace.
catch {namespace delete ::testspace};

#The namespace eval command creates a new namespace.
namespace eval ::testspace {
   variable fg "This is a namespace variable";
   
   #Export a single procedure for use later.
   namespace export tproc3
}

#Procedures can be organized into namespaces.
proc ::testspace::tproc1 {} {
   #Global procedures do not require a namespace indicator.
   testproc1;
   testproc2;

   #Procedures within the same scope do not require a 
   #namespace indicator.
   tproc2;
   tproc3;

   #Procedures from other namespaces must be explicitly declared
   #unless a namespace import is used.
   catch {oproc1} ds;
   puts $ds;
   ::otherspace::oproc1;
}

#This namespace procedure will generate an error because the
#namespace variable is not in the scope.
proc ::testspace::tproc2 {} {
   #The catch command is used to prevent the script from
   #exiting with an error.
   catch {puts $fg} g;
   puts $g;
}

#This namespace procedure will not generate an error because the
#namespace variable is localized.
proc ::testspace::tproc3 {} {
   #The variable command, if referring to an existing
   #namespace variable and not making an assignment,
   #allows local access to the variable.
   variable fg;
   catch {puts $fg} g;
   puts $g;
}

#A call to delete the namespace clears variables within
#that namespace.
catch {namespace delete ::otherspace};

#Creating a second namespace.
namespace eval ::otherspace {
   variable fh "This is a different namespace variable";
}

proc ::otherspace::oproc1 {} {
   #The namespace import command can be used to
   #localize procedures.
   namespace import ::testspace::tproc3;
   variable fh;
   catch {puts $fh} h;
   puts $h;
   tproc3;

   #namespace origin returns original namespace of the proc.
   puts [namespace origin tproc3];
}

#Run the primary procedure
::testspace::tproc1
Running these commands generates the following output. Some procedures are called more than once, so pay close attention to where the procedure call is made.
can't read "f": no such variable
This is a global variable

can't read "fg": no such variable
This is a namespace variable

invalid command name "oproc1"
This is a different namespace variable

This is a namespace variable

::testspace::tproc3