Tcl Part I · Module 11 · Scope and Namespaces
Qualified Names and How Tcl Resolves Them
Namespaced names look like file paths and behave like them: a leading double colon means start from the root, and anything else means start from where you are. That much is easy. The part worth a chapter is what happens when a name has no namespace on it at all, because Tcl's answer depends on whether the name is a command or a variable — and the two rules are different. Knowing which is which explains why puts works unqualified inside a namespace while a global variable in the same procedure does not, and it turns most namespace confusion into something you can reason about rather than guess at.
Intermediate11 min readTclPart INamespacesName resolutionScope
Part I · Module 11 · Chapter 11.8 · Qualified Names and How Tcl Resolves Them
1. Absolute and relative
A name containing :: is qualified. A leading :: makes it absolute — resolved from the root, wherever you happen to be:
namespace eval outer {
namespace eval inner {
proc who {} { return "outer::inner::who" }
}
proc call_absolute {} { return [::outer::inner::who] }
proc call_relative {} { return [inner::who] }
}
puts [outer::call_absolute]
puts [outer::call_relative]outer::inner::who
outer::inner::whoBoth reached the same procedure. The difference is where they started from: ::outer::inner::who from the root, inner::who from whatever namespace the calling procedure is in — which here is ::outer.
Run the relative one from the global scope and it fails, because there is no top-level namespace called inner:
inner::whoinvalid command name "inner::who"2. Where am I?
namespace eval probe {
proc here {} { return [namespace current] }
}
puts "inside : [probe::here]"
puts "at top : [namespace current]"inside : ::probe
at top : ::The global namespace is called ::, which is why a global variable's absolute name is $::name — that is just the empty namespace path followed by the variable.
3. The asymmetry
Now the part that matters. When a name has no :: in it, Tcl resolves commands and variables by different rules.
A command not found in the current namespace is looked up in the global one:
proc global_command {} { return "found the global command" }
namespace eval ns1 {
proc try_command {} { return [global_command] }
}
puts [ns1::try_command]found the global commandA variable is not:
set global_variable "the global value"
namespace eval ns2 {
proc try_variable {} { return $global_variable }
}
ns2::try_variablecan't read "global_variable": no such variableSame situation, opposite outcome. Qualify it and it works:
namespace eval ns3 {
proc try_qualified {} { return $::global_variable }
}
puts [ns3::try_qualified]the global value4. The command lookup order
For an unqualified command name, Tcl looks:
- in the current namespace, then
- in the global namespace.
Which is why a namespace can shadow a global command for its own calls:
proc describe {} { return "the global one" }
namespace eval shadowy {
proc describe {} { return "the namespace one" }
proc call {} { return [describe] }
}
puts "inside : [shadowy::call]"
puts "outside : [describe]"inside : the namespace one
outside : the global oneshadowy::call found describe at step 1 and never reached step 2. Code outside the namespace never sees shadowy's version at all.
5. Variables: declare or qualify
Because variable lookup does not fall back, a namespace procedure has exactly three ways to reach a variable that is not its own local:
set ::top_level "global"
namespace eval v {
variable own "namespace"
proc by_variable {} { variable own ; return $own }
proc by_namespace {} { return $::v::own }
proc by_global {} { return $::top_level }
}
puts "variable decl : [v::by_variable]"
puts "qualified ns : [v::by_namespace]"
puts "qualified :: : [v::by_global]"variable decl : namespace
qualified ns : namespace
qualified :: : global| To reach | Use |
|---|---|
| a variable in this namespace | variable name, then the short name |
| a variable in this namespace, once | $::thisns::name |
| a global variable | global name, or $::name |
There is no fourth option and no automatic search. A bare name in a procedure means a local, always.
6. namespace which
When a name is not resolving the way you expect, namespace which tells you what it actually resolves to:
namespace eval w {
proc target {} {}
variable thing 1
proc report {} {
return [list [namespace which -command target] [namespace which -variable thing]]
}
}
puts [w::report]::w::target ::w::thingIt returns the fully-qualified name, or the empty string if the name does not resolve at all. That makes it the fastest way to answer "which describe is this call going to get?" without reading the whole script.
7. A worked example
A library that deliberately uses all three forms, with a comment saying why each:
set ::verbose 0
namespace eval fmt {
variable width 24
proc set_width {n} {
variable width ;# our own state
set width $n
}
proc line {name value} {
variable width ;# our own state
# `format` is unqualified: command lookup falls back to global
return [format " %-*s %s" $width $name $value]
}
proc render {rows} {
set out {}
foreach {k v} $rows {
lappend out [line $k $v] ;# a sibling, short name is fine
}
if {$::verbose} { ;# a genuine global, qualified
lappend out " ([llength $out] rows at width $::fmt::width)"
}
return [join $out "\n"]
}
}
puts [fmt::render {cycles 12000 errors 0}]
set ::verbose 1
fmt::set_width 12
puts [fmt::render {cycles 12000 errors 0}] cycles 12000
errors 0
cycles 12000
errors 0
(2 rows at width 12)Three kinds of name in one small library: a namespace variable declared with variable, a global command used unqualified, and a global variable qualified with ::.
8. Exercises
Exercise 1. What is the difference between ::a::b and a::b?
Exercise 2. Why does inner::who work inside outer but fail at the top level?
Exercise 3. State the lookup rule for an unqualified command and for an unqualified variable.
Exercise 4. Why does puts work unqualified inside a namespace while a global variable does not?
Exercise 5. A library namespace defines open. One of its procedures calls open $path r meaning Tcl's file command. What happens, and how do you fix it?
Exercise 6. Give the three ways a namespace procedure can reach a variable that is not its own local.
Answers
1. ::a::b is absolute — resolved from the root namespace regardless of where it appears. a::b is relative — resolved starting from the current namespace, so it means different things in different places.
2. Because relative names resolve from the current namespace. Inside outer, inner::who means ::outer::inner::who. At the top level it means ::inner::who, and no top-level namespace called inner exists — so Tcl reports invalid command name "inner::who".
3. An unqualified command is looked up in the current namespace and then, if not found, in the global namespace. An unqualified variable is looked up only in the current procedure's local frame — there is no fallback at all.
4. Because puts is a command and commands fall back to the global namespace when they are not found locally. A variable name has no such fallback, so a bare name in a procedure can only ever mean a local one.
5. It gets the library's own open, because command lookup finds the current namespace first and never reaches the global one. Fix it by qualifying: ::open $path r.
6. Declare it with variable name and use the short name; qualify it as $::thisns::name; or, for a genuine global, declare it with global name or qualify it as $::name.
9. Remember
- A leading
::makes a name absolute, resolved from the root; anything else is relative to the current namespace. - The analogy to file paths holds:
::a::bis/a/b,a::bisa/b, andnamespace currentispwd. - The global namespace is called
::, which is why a global variable is$::name. - Command lookup falls back to the global namespace; variable lookup does not. This is the one rule to remember.
- So a namespace can shadow a global command for its own calls — qualify with
::namewhen you mean the built-in. - A bare variable name in a procedure always means a local. To reach anything else:
variable,global, or a qualified name. namespace which -commandand-variablereport what a name actually resolves to, or empty if it does not.
Where this fits
Part of the Tcl curriculum.
