More than just an IDE

Ins and outs of Tcl/Tk

Tcl is a fairly easy-to-learn script language, but there are a few peculiarities that might be confusing for beginners as well as for advanced users. This page explains its behavior; it is not meant as an introduction. We assume that you already have some basic knowledge about Tcl.
We hope that this page will give you a better understanding about Tcl and that you will enjoy Tcl even more.

  1. Tcl is a command language
  2. Why are control structures interpeted as commands?
  3. eval and {*}
  4. Can I use arbitrary variable names?
  5. The differences between "", {} and ()
  6. Using a " \" at the end of a line
  7. Control statements are evaluated as Tcl commands
  8. Where to place comment lines
  9. Lists
  10. Namespaces
  11. Stack levels
  12. Tcl/Tk and IPEnv


1. Tcl is a command language

This means that the first token is always interpreted as a command or procedure. As a consequence, Tcl cannot cope with assignments such as a = b + c. The solution was to define additional commands such as set and expr:

    set a [expr $b+$c]

Another consequence is, that — when applying the rules as strictly as Tcl does — control structures (if, switch, etc.), procedure definitions (proc) and comments (#) are all interpreted as commands as well. This interpretation has a few other implications. One is that comment blocks can cover several lines without the need of commenting out each line separately. For example:

    # this a some comment \
      this is some additional comment

is the same as:

    # this a some comment
    # this is some additional comment

Another implication is, that placing comment blocks in a script is more restrictive than in C:

    string first "a" $str 	# find "a" in string str
    ==> wrong # args

See Section 7 and Section 8 for more details.

2. Why are control structures interpeted as commands?

This is related to how Tcl scripts are evaluated:

Step 1: The parser starts by checking the script for unmatched quotation marks and brackets, and splits the script in groups of characters. These groups can be:

  • a group of characters not containing a single whitespace
  • a variable     (a dollar sign followed by a simple word)
  • a text string     (characters between quote characters: ""; may contain newlines)
  • a fixed string     (characters between curly brackets: {}; may contain newlines), or
  • an embedded command     (characters between square brackets: [])

Step 2: The commands are identified and are demarcated. The beginning of a command starts on a new line, or after a semicolon indicating the end of the previous command.

Step 3: Before executing a command, all the arguments are pre-processed: the variables, backslashes, commands and {*}-constructions are substituted.
Fixed strings are left unchanged, including the backslashes (except at the end of a line). Curly brackets in a text string are interpreted as ordinary characters and do not have a special meaning. They do not shield off variables ($). Hence, it is not necessary to place an escape character (\) in front of an unmatched braces. An open square bracket, however, is always interpeted as the beginning of a command, even if it is unmatched, and requires an escape character to be retained.

    set x {{a $b \n} c}
    ==> {a $b \n} c           # x is a fixed string. all characters are left unchanged
    set x "{a $b \n} c"
    ==> can't read "b": no such variable
    set x "a{b"
    ==> a{b                   # no escape character required
    set x "a[b"
    ==> (waiting for a close bracket: "[" is read as the beginning of a command)

An unmatched curly bracket in a fixed sting is ambiguous, because it coud be interpreted a a single character or as a delimiter. If it is intended as a literal bracket, then it must be preceded by a backslash. The same is true for quote characters in a text string. The main difference between both types of strings is that the escape characters in a text string are automatically interpreted, whereas in fixed strings you must use the subst command (see Section 5 for details.

Step 4: The commands are executed.
This cycle (step 1-4) is repeated recursively to resolve nested commands. Nested commands are always executed first.
Example
Suppose that x equals {1 2 3 4}.
puts [lrange $x 1 end] is parsed as:

    "puts [lrange $x 1 end]"      # this is the input string
    puts [lrange $x 1 end]        # 1. splits the string into two arguments
                                  # 2. interprets the string as a single command
    puts [lrange {1 2 3 4} 1 end] # 3. substitutes variable x
    puts {2 3 4}                  # 4. executes lrange
    ==> 2 3 4                     # 4. executes puts

Steps 1-3 are executed without any knowledge of the commands. This mechanism has the advantage that the command name can also be a variable, or a user-defined procedure not yet available during compilation. Suppose, for example, that we have a variable that is used as a command name. If there is an escape character at the end of the line, this line and the next one are concatenated, independent of the command itself. So inevitably, both lines are always evaluated as a single command:

    set cmd "list"
    $cmd arguments of $cmd\
        continue on next line
    ==> arguments of list continue on next line

Now, if we set cmd to "#", it will still be executed in the same way:

    set cmd "#"
    $cmd arguments of $cmd\
        continue on next line
    ==> (empty line)

It is not recommended to apply these constructions arbitrarily, but they are essential to Tk and other Tcl extentions in which the widgets are dynamically create and their names are stored in Tcl variables. Widget names are nothing else but Tcl command of a special kind.

Remarks
Note that although system error messages might look the same, they can be generated on a different level. In the following example, a fixed string is passed to subst. subst analyzes the string and throws an error at step 4:

    subst {$a b}
    ==> can't read "a": no such variable

If the same input would be passed as a text string, the error message is caused by the parser at step 3, but with the same results:

    subst "$a b"
    ==> can't read "a": no such variable

Expressions
If you want to use expr to compare strings, you might have got confusing error messages. Suppose you want to evaluate an option and stored the result in a boolean as:

    set opt "HIDE"
    set show [expr $opt eq "SHOW" ]

This looks OK, but you will get the following error message:

    invalid bareword "HIDE"
    in expression "HIDE eq SHOW";
    should be "$HIDE" or "{HIDE}" or "HIDE(...)" or ...
        (parsing expression "HIDE eq SHOW")

By definition, there is nothing wrong with the syntax, but what does go wrong is the way the equation is resolved. First, the parser substitutes $opt and the quotation marks around SHOW are dropped (i.e. step 3), leading to the incorrect expression:

    expr HIDE eq SHOW

So, if you want to keep it safe, use curly brackets to guarantee that the expression is passed to expr unmodified, and let expr do the work. Therefore, the preferred syntax is:

    set show [expr {$opt eq "SHOW"} ]

There is no need to place quotation marks around $opt.

Bindings
The bind command is a special case, because the binding script is postponed till the right event occurs. The script is not compiled and is executed in the global namespace. This implies that the variables created in the script, automatically become global variables and are retained after the script has finished:

    bind $path <1> {
        set pos [%W index @%x,%y]
        # pos becomes the new global variable ::pos
    }

If you want to use local variables in the script, you can place quotation marks or the list or subst command during compilation. They will be replaced by constant values. For example:

    set path .f
    bind $path.c <1> "foo $path"

stores the binding script as: "foo .f". The local variable path will no longer be available.

3. eval and {*}

If you want to execute a command that is stored in a string, you can use eval. If you have, for example, an interface that reads the command arguments from an entry widget and the options from a combobox, the command can be executed as:

    eval cmd -opt $value $arguments

eval concatenates all arguments, i.e. it interprets each index of each argument as a separate argument of cmd. This might cause a problem if value is a multiple-word text string that you would like to retain as a single argument. Putting $value between quote characters wouldn't make a difference, because it would still be interpreted as a multiple argument list, whereas using curly brackets would inhibit a variable substitution. This can be solved by using the list command, either to group $value:

    eval cmd -opt [list $value] $arguments

or the entire command as:

    eval [list cmd -opt $value $arguments]

What happens is that eval cancels out the list command and as a result the original command is retained.
With Tcl version 8.5, a new syntax element was introduced that resolves this annoyance. Instead of using the eval command, now you can write "{*}" in front of the arguments that need to be split. The command above can be rewritten as:

    cmd -opt $value {*}$arguments

{*} can be used more than once in the same command and is not related to cmd:
Another example to show its usage:

    set str "strerr test"
    puts $str
    ==> stderr test
    eval puts $str
    ==> test

or use {*}:

    puts {*}$str
    ==> test

The eval command still remains useful, for example, if you want to execute a command line that is read from a console as a whole.

4. Can I use arbitrary variable names?

• A variable name can be an arbitrary string of characters. Names such as "5", "a+b" or even "\$x; {2" are completely legal, but not recommended. Be aware that a variable name can also be an empty string. As a beginnner, it might be confusing to get can get cryptic error messages such as: can't read "": no such variable, but it just means exactly what is says. Suppose for example, that y already exists as an empty string and mistakenly you wrote "set $y" instead of "set y", then you would get this error message.
The syntax is alright, but you will get a runtime error.

• You can concatenate variables by writing them without whitespaces in between: $str1$str2, or by using the concat command.
Instead of "set str3 $str1$str2" you could also write "concat str3 $str1 $str2" (assuming that str3 is an empty string or does not yet exist).
If it is not clear where a variable name ends, you can use curly brackets to demarcate the variable name. All operators and punctuation character, except for "::" are natural delimiters. You will get an error message if you write "$z::", because z will be interpreted as a namespace. If your really want to substitute $z, you must write: "${z}::". A single colon, however, acs as a normal delimiter: "$z:".
You can also use append to concatenate text strings:

    set y $var
    append y "str"

or:

    append y $var "str"; # (assumes that y is empty or does yet not exist)

This is the same as writing:
set y ${var}str
Note that you cannot write set y $var"str1 str2", because the quote characters are interpreted as literal charaters and the whitespace between str1 and str2 acts as a separator between the arguments. If you want to append a string containing whitespaces to another variable, then you must use escape characters instead:

    set var 0
    set y $var" 1 2 3"
    ==> wrong # args: should be "set varName ?newValue?"
    set y $var"\ 1\ 2\ 3"
    ==> 0" 1 2 3"      # quote characters are still present
    set y $var\ 1\ 2\ 3
    ==> 0 1 2 3        # OK
    set y $var"
    ==> 0"

As a matter of course, using append is alway a better solution.

5. The differences between "", {} and ()

Brackets, (), are used to indicate array indices. Both quotation marks, "", and curly brackets, {}, are used to mark the beginning and the end of a string or a list. The difference is that curly brackets inhibit substitution of embedded variables, commands, and backslashes during runtime. This does not mean, however, that a fixed string is always passed as is. The exception is, when a backslash is placed at the end of a line. In this case, the backslash and the leading whitespaces on the next line are replaced by a single space:

    set v {a\
	   b}
    ==> a b

Backslash substitution is the same for text strings and fixed strings:

    set v "a\
	   b"
    ==> a b

If you do not use a backslash, all character between the quotation marks or braces — including the newlines — are retained:

    string length {a\
	   b}
    ==> 3
    string length {a
	   b}
    ==> 10           # this includes the newline

Hence, a backslash at the end of a line has a different meaning as in C (see below). In C, a backslash is used to indicate that the string continues on the next line; the leading spaces on the new line are retained. And, secondly, a backslash in C is mandatory whereas in Tcl, a string can cover several lines without special notice.

Note that curly brackets inside quotation marks do not inhibit variable subtitution:

    set v "hello"
    set v2 {$v}
    ==> $v
    set v2 "{$v}"
    ==> {hello}
    string length {$v2}
    ==> 3
    string length "{$v2}"
    ==> 7

The reason is that substitution takes place before the set command is executed. The only characters in a string that have a special meaning are \, $ and [ ]. Curly brackets are treated as ordinary characters.

In most cases, quotation marks around a single word can be left out. But there are two exceptions. The first one are strings in expressions. The second case is if a word contains a semicolon. For example,

    append var v1 ; v2

gives an error, because the semicolon is interpreted as a command separator and v2 is interpreted as a command. You should write:

    append var "v1" ";" "v2"

Curly brackets cannot be used to define a string containing a single curly bracket:

    set x {\{}
    ==> \{

So, if you want to locate a bracket in a string, you must write:
string first "\{" $txt
and not
string first {\{} $txt

If you do not insert a backslash, the character is read as an unmatched brace, but if you do so, the backslash is retained and you cannot get rid of it. This contradiction can only be solved afterwards, by using the subst command:

   set x {a\{b}
   => a\{b
   subst $x
   ==> a{b

subst will replace all backslashes, commands and variables. If you want to retain commands or variables in a fixed string, you must add the -nobackslash, -nocommands and/or -novariable option to the subst command to inhibit complete substitution:

    set x {a\{b $c}
    ==> a\{b $c
    subst $x
    ==> can't read "c": no such variable
    subst -novariable $x
    ==> a{b $c

A literal square bracket in a fixed string does not need to be preceeded by a backslash. Note, however, that if a string contains an unmatched square bracket that is not preceded by a backslash and you want to apply subst, you will also need to add the -nocommand option as well:

    set c "something"
    set x {a[b $c}
    ==> a[b $c
    subst $x
    ==> missing close-bracket
    subst -nocommand $x
    ==> a[b something

6. Using a " \" at the end of a line

There are three cases where backslashes to escape a newline is meaningful.

• The first one is already mentioned above: it can be used to remove redundant leading spaces in a string covering more than a single line. Note that the whitespaces in front of the backslash are not affected. A backslash does not require a delimiter in font:

    set x {a \
	   b}
    ==> a  b (two spaces in between. not just one)

If you only want to get rid of the leading spaces, but still want to retain the newlines, you can write:

    set txt "line1\n\
        line2\n\
        line3"

• The second case in which you need to write backslashes is, when you want to continue a command on the next line:

    command arg1 arg2 \
        arg3 arg4 ...

Without a preceding backslash, arg3 will be interpreted as a new command.
This construction may also be hidden in an embedded command:

    array set test [list \
        index1 val1 \
        index2 $val2 \
        index3 "$val3 $val4" \
    ]

The backslashes are needed, not to group the text between the square brackets — brackets are always matched automatically — but to indicate that the list command does not stop at the end of the line. Hence, the following syntax is also correct (although not recommended):

     array set test [
	 list \
	 index1 val1 \
	 index2 $val2 \
	 index3 "$val3 $val4"
     ]
 

The best choice is to swap the quotation marks and the list command and leave out the backslashes:

     array set test "
        index1 val1
        index2 $val2
        index3 [list $val3 $val4]
     "

• The third case is a trick to let Tcl scripts behave differently under bash and in Tcl:

    #!/bin/sh
    # The following line is hidden in Tcl, but not in bash \
    exec wish $0 ${1+"$@"}
    # Tcl commands follow

If you source this script in wish, exec is hidden and the Tcl commands following are evaluated. If you run this script in bash, then exec is executed and launches the current script in wish. wish will be run in the foreground. This will block further evaluation of the Tcl script. If you exit wish, this script will also end. The advantage is that you can treat the script name as a bash command by setting the access mode, and do not need to call wish explicitly.

In all other cases, a continuation token is redundant. You may read, for example, constructions such as:

    package ifneeded tdbc 1.0.6 \
        "package require TclOO 0.6-;\
        [list load [file join $dir tdbc106t.dll]]\;\
        [list source [file join $dir tdbc.tcl]]"

The first backslash is required to indicate that the subsequent lines are part of the package command. But the other backslashes are redundant, because the quotation marks already group these lines. Having placed the backslashes, however, the newlines — acting automatically as command delimiters — were removed and you are forced to insert additional semicolons as replacements. The list commands in this example are needed to hold the file names together in a single string (see: the eval command).
A clearer syntax would be:

    package ifneeded tdbc 1.0.6 [subst {
        package require TclOO 0.6-
        load "[file join $dir tdbc106t.dll]"
        source "[file join $dir tdbc.tcl]"
    } ]

Note the additional whitespace between } and ] on the last line.

7. Control statements are evaluated as Tcl commands

Interpreting control statements as commands has a few consequences.

• Conditions and arguments need to be placed in curly brackets instead of parentheses as in other languages.

• Sometimes you can see:

    proc fnc x {...}; # no curly brackets around the argument

instead of:

    proc fnc {x} {...}

This is correct, but it is a sloppy style of programming and is not recommended because it makes the code less readable. Moreover, leaving out curly brackets may not always be correct, as we saw in the expr example above.
The following code is executed correctly:

    if $x {
	# do something
    }

but for different reasons: $x is replaced by its value before the if command is evaluated (step 3). If you place curly brackets around $x, then $x is evaluated by the if command itself (step 4). As a user you will not notice the difference. If we would execute the following code, however, leaving out the curly brackets, we would end up in an infinite loop, printing "9" indefinitely:

    set i 10
    while [incr i -1] {
	puts $i
    }

The switch statement is programmed differently, and does NOT evaluate the flag. It is replaced in advance before the statement is executed, and curly brackets are definitely wrong:

    switch {$x} {	# compares the string "$x" and not its value
	# switches
    }

• Why can't we just write:

    if {$a == str} {...}

instead of:

    if {$a == "str"} {...}

It is because the condition is passed as a fixed string to the if command and is evaluated as an expression and not as a list.
This means that conditions do not require a strict mathematical format, and can contain any expression or procdedure that returns an integer. For example:

    # x contains an integer
    if {$x-5} {
	puts {$x does not equal 5}
    } else {
	puts {$x equals 5}
    }

Similarly, instead of:

    set j 1
    for {set i 10} {$j != 0} {incr i -1} {
	puts "$i $j"
	set j $i
    }

we could also write:

    for {set i 10; set j 1} {$j} {set j $i; incr i -1} {
	puts "$i $j"
    }

The first and third argument are evaluated as a code block, the second argument is evaluated as an expression.

All variables in expressions, as well as the output of commands and procedures, are automatically interpreted either as a string or a number. Placing additional quotation marks around variables and commands are redundant. Quotation marks or curly brackets are needed to indicate that a group of characters must be interpreted as a string. This is also the case for macros (%W etc.) in bindings. If a binding is executed, the macros are substituted by their values. If the macro represents a string, it does not include the quotation marks. Hence, leaving out the quotation marks in an expression will raise an error:

    bind $path <Event> {
        if {"[foo %W]" == ".widget"} { # redundant quotation marks around []
                                       # no quotation marks needed around %W
            do something
        }
        if {%W == ".widget"} {         # syntax error. %W requires quotation marks
            do something
        }
    }

8. Where to place comment lines

• Positioning comment lines is more restrictive than in C. It might help to realize that comments are just special Tcl commands. You wouldn't write:

    string first "a" $str 	set str "test"

then why writing:

    string first "a" $str 	# set str "test"
    ==> wrong # args: should be "string first needleString haystackString ?startIndex?"

The comment line is parsed as the arguments of string. You can solve this problem by inserting a semicolon between the two commands after $str.

Generally, you can always start a comment string on a new line. This can be anywhere in a script, but not inside an argument list or an argument, except in the body of a statement. This also includes the switch statement:

    switch $n { # comment here is ok
      # comment here is also ok
      1 {... commands ...}
      # you can comment out a case option
      # 2 {... commands ...}
    }

• The parser checks whether the command is complete and this includes a comment block. As a result, commenting out a control statement as follows goes wrong:

    # while {$x} {
	(do something)
    # }
    ==> unmatched open brace in list

You can add a backslash in front of the unmatched open braces in the comment lines. An unmatched close brace does not cause any problem.

9. Lists

A list is not an intrinsic property of a string; it is just another interpretation. This interpretation is made by the command, and not by the parser. For example, lindex interprets the first argument as a list, whereas split interprets the same string as a fixed text:

    lindex {{$a b} {c d}} 0;   # interprets argument 1 as a list
    ==> a $b

but:

    split {{$a b} {c d}};      # interprets the same argument as a string
    ==> \{\$a b\} \{c d\};     # list of four elements

• A list can be created by parts as an ordinary string. The interpretation of the string comes afterwards:

    set lst {"a $b"}
    append lst " \{"
    append lst "c \$d\}"
    puts $lst
    ==> "a $b" {c $d}
    # we can read lst as an ordinary list:
    lindex $lst 0
    ==> a $b
    lindex $lst 1
    ==> c $d

• The lists

    {"a $b" {c $d}}

and

    {{a $b} {c $d}}

are identical. The quotation marks and braces just demarcate the beginning and the end of the elements. In both cases, the variables are not substituted.

However, the lists

    {{a b} {c d}}

and

    {{a b}   {c    d}}

are identical, only as far as the base elements, a, b, c, and d are concerned; i.e.

    [lindex {{a b} {c d}} 1 1] == [lindex {{a b} {c   d}} 1 1]

but

    [lindex {{a b} {c d}} 1] != [lindex {{a b} {c   d}} 1]

This is because the output of lindex are strings, and they are not compaired as a list of elements.

• In addition to quotation marks and curly brackets, you can also group the elements by using backslashes in front of the whitespaces in the elements. The string {a\ $b c\ $d} represents the same list above containing two elements.
Backslashes are automatically inserted if the string contains braces. For example:

    list "a { b"
    ==> a\ \{\ b

this, in contrast to:

    list "a b c"
    ==> {a b c}

The reason is that curly brackets cannot be used to demarcate fixed strings containing unmatched open braces (see Section 5). The backslashes can be seen as a necessary internal representation. If you stick to the list commands, you might not even notice them at all:

    lindex [list "a { b" "c d" ] 0;	# same list as above
    ==> a { b

Not all strings can be interpreted as lists. A list requires that the string does not contain unmatched braces or quotation marks.
If you write:

    subst {\[ \{ (}
    ==> [ { (

the output cannot be interpreted as a list. Note that "{\[ \{ (}" already is a list containing braces and brackets, and it does not require substitution to be interpreted correctly:

    lindex {\[ \{ (} 0
    ==> [
    lindex [subst {\[ \{ (} ] 0
    ==> unmatched open brace in list

Example
If you want to get the procedure names in a Tcl script by checking it line by line, then the following method will fail:

    if {[regexp {\s*proc } $line]} { # check the lines starting with "proc"
        # the next command will fail because $line is not complete:
        lappend procs [lindex $line 1]
    }

You can check completeness with "info complete $string".

• If a variables is substituted then the replacement will still be interpreted as a single group of characters. So, foo "$x" and foo $x are identical. This is not the case for bindings. For example:

    set str "Hello World"
    bind $path <1> "puts $str"

will evoke an error. The reason is that the binding script is evaluated during runtime, whereas substitution takes place in an earlier stage during compilation. Consequently, the executable script will read: "puts Hello World!". To avoid this problem, you can write:

    bind .b <1> [list puts $str]

The list command is useful only if the script contains local variables. Otherwise, it is always better to use curly brackets. Just write:

    bind Tree <FocusIn> {
        after idle BWidget::refocus %W %W.c
    }

instead of:

    bind Tree <FocusIn> [list after idle {BWidget::refocus %W %W.c}]

• Generally, quotes do not preserve list elements. For example:

    set var1 "a b"
    set var2 "c d"
    set lst "$var1 $var2"
    ==> a b c d   # four elements
    set lst [list $var1 $var2]
    ==> {a b} {c d}

Hence,  set lst "$var1 $var2"  is equivalent to  set lst [concat $var1 $var2] . So, if you write  lappend lst "$var1 $var2"  you do not append an element containing two, but four elements to lst.

• Tcl contains a set of commands, specially designed to process lists. They are:

    lappend lassign lindex linsert llength lmap lrange lreplace lreverse lsearch lset lsort

The first argument of these commands always represents a list.

lappend and lset read the list by name, the other commands read the list as a variable or as a literal list.
lappend and lset return the modified list, both as an argument and as a return value:

    set lst2 [lappend lst1 elem]
    # lst1 and lst2 are identical. lst1 is also modified

The list commands reformat input string to a list according to the standard form using curly brackets. The spaces in between the elements are replaced by a single character, but all elements remain untouched:

    set lst {"a $b"    {c   $d  }  }
    lappend lst "e f"
    ==> {a $b} {c   $d  } {e f}

So, {c  $d  } is not replaced by {c $d}, because it would mean that the parser would interpret the element as a sub-list. Although the shape might suggest so, this is not how Tcl lists are defined.

• There are four Tcl commands that create new lists. They are: concat, lrepeat, split, and list.

10. Namespaces

A namespace is created as namespace eval name script. The script can be used to create namespace variables and namespace procedures, or execute commands, or can be empty.

• Once a namespace is created, you can dynamically add and delete as many procedures and variables as needed. This can be done in another namespace eval, or you can use set and proc, writing the namespace name expicitly. The following scripts are identical:

    namespace eval ns {
        # the commands are executed inside a namespace
        variable x 10;    # creates ns::x
        set y 0;          # creates ns::y
        proc fnc {} {     # creates ns::fnc
            puts "1   creates fnc2 in namespace [namespace current]"
            proc fnc2 {} {
                # this is ::ns::fnc2
                set frameinfo [info frame [info frame]]
                puts "2   runs: [dict get $frameinfo proc]"
                namespace eval :: {
                    puts "    evaluates script in global namespace\n   \
                        creates fnc2 in namespace [namespace current]"
                    proc fnc2 {} {
                        # this is ::fnc2
                        set frameinfo [info frame [info frame]]
			puts "3   runs: [dict get $frameinfo proc]"
                    }
                }
            }
        }
        namespace eval ns2 {puts [list creates namespace [namespace current]]}
        puts "Calling ns::fnc"
        fnc;              # executes ns::fnc; creates ns::fnc2
        puts "Calling ns::fnc2"
        fnc2;             # executes ns2::fnc2; creates ::fnc2
        puts "Calling ::fnc2"
        ::fnc2;           # executes ::fnc2
    }

and:

    # all commands are executed in the global namespace. This only shows the framework
    namespace eval ns {}
    set ns::x 10
    set ns::y 0
    proc ns::fnc {} {
        puts "proc ::ns::fnc"
        proc fnc2 {} {puts "proc ::ns::fnc2"}
        namespace eval :: {
            proc fnc2 {} {puts "proc ::fnc2"}
        }
    }
    namespace eval ns::ns2 {puts "namespace ::ns::ns2"}
    ns::fnc
    ns::fnc2
    fnc2

• All procedures and variables that are created by set, array, source, and inside proc and "namespace eval" are added to the current namespace. If this is not what you meant, then it can be overruled by calling "namespace eval {} {... commands ...}" or "namespace eval :: {...}" from within another namespace.

• The variable command declares a variable in a namespace, but if you do not assign a value to the variable, it cannot be accessed and unset:

    namespace eval ns {variable var}
    info vars ns::*
    ==> ::ns::var    # i.e. ns::var exists
    set ns::var
    ==> can't read "ns::var": no such variable
    unset ns::var
    ==> can't unset "ns::var": no such variable

• Global variables and procedures are stored in the global namespace. Its name is the empty string. Write ::varname to access a global variable from within another namespace or procedure, or to declare it as a global variable. You can also use the global statement and leave out ::.

• An callback event is always executed in the global namespace. If you create a variable in a binding, it will be retained as a global variable. Moreover, you can access global variables without adding ::. For example:

    set ::globvar 0
    bind $w <Button-1> {
        incr globvar
        puts " \$globvar and \$::globvar are the same variables: $globvar $::globvar"
    }

11. Stack levels

If you enter a procedure you will go to the next stack level. This is also the case inside a "namespace eval". The top level is the global namespace, and is level 0. The Tcl commands, upvar and uplevel, give full access to other stack levels.
For example, if you want to know the namespace from which a procedure is called, from within the procedure itself, you can write: "set ns [uplevel ::namespace current]".

uplevel
uplevel concatenates its arguments and executes the string in the stack frame indicated. The default uplevel is 1.
You may see constructions such as: "set ns [uplevel 1 [list ::namespace current]]", but both the uplevel count "1" and the list command are redundant and, for the sake of clearity, should be left out. Moreover, you can apply the same rules for uplevel as for eval, and use the list command only for arguments containing multiple words or a list that must be retained. For example, instead of:

    uplevel 1 [list catch [lreplace $args 0 0 [lindex $cmds $n]] ::result ::opt]

(meaning: replace the command argument in args by the n-th element is a list of commands, and execute the command in the stack level above),
you can write:

    uplevel catch [list [lreplace $args 0 0 [lindex $cmds $n]]] ::result ::opt

Note that the list command is needed, even if you would execute the command by steps:

    set cmd [lreplace $args 0 0 [lindex $cmds $n]]
    uplevel catch [list $cmd] ::result ::opt

This is due to how concatenation in Tcl is defined:

    set var val1; set lst {val2 val3}
    concat $var $lst
    ==> val1 val2 val3
    concat $var [list $lst]
    ==> val1 {val2 val3}

upvar
The most common implementation of upvar is to pass procedure arguments by pointer. Actually, there are no pointers in Tcl, but they are simulated by upvar as follows:

    proc copy {var1 var2} {
        upvar $var2 lvar2
        set lvar2 $var1
    }
    set var1 10
    copy $var1 var2
    set var2
    ==> 10

upvar links a local variable lvar2 with a variable in the other stack frame. upvar does not create a new variable, and $arg2 (i.e. var2) does not already need to exist before copy is called: setting lvar2 in the copy procedure will also change or create var2.
You could also write:

    proc copy {var1 var2} {
        upvar var2 lvar2
        set lvar2 $var1
    }
    set var1 10
    copy $var1 var2
    set var2
    ==> 10

Seemingly with the same results, but the argument var2 in copy merely acts as a dummy and is not referenced at all. Calling copy $var1 var3 will also create var2

12. Tcl/Tk and IPEnv

IPEnv is a multilingual IDE, but it has strong roots in Tcl/Tk. If you are programming in Tcl/Tk, you can profit from all the advantages. that IPEnv has to offer: sophisticated automatic code completion, clever goto and find functions and it recognizes Tcl procedure prototypes. You can load your own plug-ins written in Tcl/Tk.
IPEnv has a built-in multi-threaded console in which you can execute Tcl/Tk commands, interrupt Tcl commands and system commands as well as, run Tcl scripts in the background and much more.
The advantages of IPEnv over wish:

  • IPEnv has an advanced Tcl language editor;
  • You can change the working directory with a mouse click;
  • IPEnv has a mature interactive console:
  • Tcl scripts can be interrupted [Ctrl+C] and you can run scripts in the background [&];
  • IPEnv is multi-threaded. You can continue editing while a Tcl script is running, and system commands can be interrupted and do not block the interface and can be interrupted;
  • Tcl output can be redirected to a file;
  • Commands such as ls generate hyperlinked output, giving a easy access to the files;
  • IPEnv does not close down on exit commands in a script;
  • IPEnv has a fully-integrated editor, console and file browser;
  • IPEnv gives direct access to TclProDebugger and automatically loads the scripts into the debugger.
  • extend IPEnv with your own plug-ins.

Just give it a try and you will realize what you are missing.
IPEnv comes with the latest stable version of Tcl/Tk, and includes several additional Tcl extensions, including BLT and TclProDebugger.