Skip to content
VLSI Mentor

Tcl Part I · Module 11 · Scope and Namespaces

variable: State That Belongs to a Namespace

The previous chapter gave a group of procedures a shared home and stopped deliberately short of the state that goes with them. Namespace variables need their own chapter because of one mistake that is extremely easy to make and produces no error at all: reaching for global inside a namespace procedure. It looks right, it runs, and it quietly operates on a different variable from the one the namespace declared — so the library ends up with two copies of its own state, one of which is never updated and one of which nobody reads. This chapter shows both the correct command and that failure, side by side.

Intermediate11 min readTclPart INamespacesvariableState

Part I · Module 11 · Chapter 11.7 · variable: State That Belongs to a Namespace

1. Declaring one

variable inside a namespace eval creates a variable belonging to that namespace:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval counter {
    variable count 0
}

puts $::counter::count
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0

Its full name is ::counter::count. It is not a global, and it is not local to anything — it belongs to the namespace and lives as long as the namespace does.

2. Procedures must declare it too

A procedure in the namespace does not automatically see the namespace's variables:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval c1 {
    variable hidden 7
    proc peek {} {
        return [info exists hidden]
    }
}
puts [c1::peek]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0

Zero. The variable is right there in the same namespace and the procedure cannot see it, for the same reason a procedure cannot see a global in Chapter 11.2: a procedure's frame contains only what it declares or sets.

variable inside the procedure makes the link:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval c2 {
    variable hidden 7
    proc peek {} {
        variable hidden
        return "exists: [info exists hidden], value: $hidden"
    }
}
puts [c2::peek]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
exists: 1, value: 7

3. The correct counter

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval hits {
    variable count 0

    proc bump {} {
        variable count
        incr count
        return $count
    }

    proc total {} {
        variable count
        return $count
    }
}

hits::bump
hits::bump
hits::bump
puts "total        : [hits::total]"
puts "the variable : $::hits::count"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
total        : 3
the variable : 3

Each procedure declares what it touches on its first line, and both see the same variable.

4. The mistake: global instead of variable

Now the same namespace with one word changed:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval broken {
    variable count 0

    proc bump {} {
        global count          ;# <- wrong command
        incr count
        return $count
    }
}

broken::bump
broken::bump

puts "the namespace variable : $::broken::count"
puts "the GLOBAL variable    : $::count"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
the namespace variable : 0
the GLOBAL variable    : 2

Read both lines. broken::bump returned sensible-looking numbers, raised no error, and incremented the global count — which it also created. The namespace's own count is still 0 and will stay there forever.

5. Telling them apart

The two commands differ in exactly one respect — which namespace they look in:

Looks inUse inside a namespace procedure
global namethe global namespace, ::only for genuine globals such as env
variable namethe procedure's own namespacefor the library's own state

A quick way to check which you have: print both names.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval probe {
    variable value "namespace copy"
    proc report {} {
        variable value
        return $value
    }
}
set ::value "global copy"

puts "namespace : $::probe::value"
puts "global    : $::value"
puts "procedure : [probe::report]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace : namespace copy
global    : global copy
procedure : namespace copy

If a procedure that should report namespace copy reports global copy, it declared the wrong one.

6. Reaching one from outside

A fully-qualified name works anywhere, with no declaration:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval settings {
    variable mode "fast"
}

proc show_mode {} {
    return $::settings::mode
}
puts [show_mode]

set ::settings::mode "slow"
puts [show_mode]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
fast
slow

That is the same ::-prefix idea as Chapter 11.2 §5, with a longer path. It is the right tool for an occasional read from outside the library; inside the library, variable is clearer.

7. A worked example

A library with private state and a public interface:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval stats {
    variable total 0
    variable count 0

    proc add {value} {
        variable total
        variable count
        incr total $value
        incr count
        return $count
    }

    proc mean {} {
        variable total
        variable count
        if {$count == 0} { return "no data" }
        return [format %.2f [expr {$total / double($count)}]]
    }

    proc reset {} {
        variable total
        variable count
        set total 0
        set count 0
    }
}

foreach v {10 20 30 40} { stats::add $v }
puts "count : [stats::add 0]"
puts "mean  : [stats::mean]"

stats::reset
puts "after reset : [stats::mean]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
count : 5
mean  : 20.00
after reset : no data

Three procedures, two shared variables, each declared where it is used. Nothing outside the namespace needs to know the variables exist.

8. Exercises

Exercise 1. What does variable count 0 do at namespace level, and what does variable count do inside a procedure?

Exercise 2. Why does this print 0?

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval n { variable v 7 ; proc peek {} { return [info exists v] } }
puts [n::peek]

Exercise 3. What exactly goes wrong when a namespace procedure uses global count instead of variable count, and what is the error message?

Exercise 4. Write a namespace log with a variable lines and procedures to append a line and return the count.

Exercise 5. How do you read a namespace variable from outside the namespace?

Exercise 6. A library's counter procedure returns increasing numbers, but another procedure in the same library reports the count as 0. What is the likely cause?

Answers

1. At namespace level it creates the variable and gives it an initial value. Inside a procedure it declares a link to the existing namespace variable, creating no value of its own — the same division of labour as global.

2. Because a procedure does not automatically see its namespace's variables. Its frame contains only its parameters and whatever it declares or sets, so v is not there until variable v links it.

3. global count links to ::count, the variable in the global namespace, not ::n::count. The procedure then increments the global — creating it if needed — while the namespace's own variable stays at its initial value. There is no error message, which is what makes it dangerous: the procedure's return value looks correct and only the namespace variable disagrees.

4.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
namespace eval log {
    variable lines {}

    proc add {text} {
        variable lines
        lappend lines $text
        return [llength $lines]
    }

    proc count {} {
        variable lines
        return [llength $lines]
    }
}

log::add "first"
log::add "second"
puts [log::count]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2

5. With its fully-qualified name, $::namespace::variable, which needs no declaration. Better still, through a procedure the namespace provides, so the variable does not become part of the public interface.

6. One of them used global where it should have used variable. The incrementing procedure is almost certainly updating ::count while the reporting one reads ::library::count, or the reverse — two separate variables, no error.

9. Remember

  • variable name value at namespace level creates the variable; its full name is ::namespace::name.
  • variable name inside a procedure declares a link to it — a procedure does not see its namespace's variables automatically.
  • global name reaches the global namespace, not the enclosing one. Using it inside a namespace procedure is almost always wrong.
  • That mistake raises no error. The procedure updates a global of the same name while the namespace variable stays at its initial value — two copies, silently.
  • The symptom is two procedures in one library disagreeing about a value that only one of them is updating.
  • Reserve global inside a namespace for genuine globals such as env and tcl_platform.
  • A fully-qualified $::ns::var reads one from outside with no declaration.
  • Prefer exposing state through procedures, so the variable does not become permanent public interface.

Where this fits

Part of the Tcl curriculum.