Tcl variable scope - how does Tcl control when a variable is seen?

I was noticing a discussion on comp.lang.tcl about variable scope and when I searched the wiki, I didn't notice quickly any place where the topic of scope was centrally discussed.


What is scope? In programming languages, it is the idea of where the value of a variable is known.

For instance, in Tcl, you might have the following:

#! /usr/local/bin/tclsh
set gvar 123 ; # this is a variable created in the global namespace

proc nest1 {} {
    set lvar {This is a test}
    puts $lvar         ; # this variable is local to nest
    puts $gvar
}

nest1
puts $lvar

which will raise an error when nest attempts to print gvar as coded above. This is because even though the gvar variable was created in the global namespace, nest won't look at variables declared there without the coder taking special action.

So, now we will try the code again:

proc nest2 {} {
    global gvar
    set lvar {This is a test}
    puts $lvar         ; # this variable is local to nest
    puts $gvar
}

nest2
puts "lvar 2nd print $lvar"

Now nest2 should work fine - because tclsh was told by the global call that it should look in the global namespace when references to gvar occur.

However, the 2nd print of lvar will fail. lvar was created within nest2, so it is not available to print outside of the proc. The only way to let some other proc, or code in the global namespace, access lvar is to put it into the global namespace.

There is a second way to put variables into a specific namespace. Here's nest3, which puts both variables into the global namespace.

proc nest3 {} {
    set ::lvar {This is a test}
    puts $::lvar               ; # this variable is local to nest
    puts $::gvar
}

nest3
puts "lvar 2nd print $::lvar"

Currently, using the namespace variable notation apparently is a slight performance hit, but using a consistent notation may be worth not having to remember to code global statements...


Tcl has several layers of scoping - from an article by Bryan Oakley, we read:

 Sektor van Skijlen wrote:
 > Could someone explain me, what is the proper syntax with correct scope for
 > variables, which are:
 > 
 >  - declared inside in a namespace

variable varname ?initValue?

 >  - globally

global varname ?varname ...?


LV: With the advent of namespaces, one can provide a more consistent variable usage by using ::varname as opposed to invoking the global command with varname.

RHS: However, if you're accessing the variable a lot, it's worth using the global command, since it is faster to access the variable that way. Assuming performance matters in what you are doing.

% proc p1 {} { global v1 ; for {set i 0} {$i <1000} {incr i} { set v1 } }
% proc p2 {} {for {set i 0} {$i <1000} {incr i} { set ::v2 } }
% time { p1 } 100
1802 microseconds per iteration
% time { p2 } 100
4520 microseconds per iteration

schofield:

Consider the above procs. Both p1 & p2 access the variable v1 & v2, respectively, many times. Should you only need to access the variable once or maybe twice, it is faster to fully qualify the variable name and pay the price of having tcl look up the variable, which is cheaper than the "global" call.

%  proc p3 {} {global v3; set v3}
%  proc p4 {} {set ::v4}
%  set v3 1
1
%  set v4 1
1
% time p3 100
41 microseconds per iteration
% time p4 100
33 microseconds per iteration
% proc g1 {} {global v3}
% time g1 100
51 microseconds per iteration

What I'd like to know is why is it more expensive to "global v3" than it is to "global v3; set v3"? This happened consistently for with many "time" invocations.


LV: Curious - is it due to additional parsing? Or is Tcl just missing an optimization?

RHS: My guess (and it's just a guess) is that, when it goes to look up the variable:

  • For the one linked via global, it's now a local variable so it's in the local table. This means that it finds the variable immediately
  • For the qualified name, it has to parse the name, recognize the namespace it's in, and then go look in that namespace's table to get the value

As a sidenote, the bytecode is pretty much the same for them (other than the global command part), so the issue isn't there.


> - accessed in a proc defined in the same namespace

use variable if you wish to reference the namespace variable, global if you want to reference the global variable. Use neither to reference a local variable.


Please add other information, discussion, corrections to this page.


Note that merely declaring a variable with the global or variable command does NOT create the variable. It just tells Tcl where to go looking for the variable if there's a reference to it.