2

Upvar creates a link to a variable in a different stack frame, sometimes called a call stack, or a different scope.

Upvar is also used to create an alias for a global (or namespace) variable 2. But a namespace is created only by the namespace eval command. A new stack frame is created by the proc command.

Namespace and call stacks appear to be two ways that the TCL naming context can change. Upvar and Uplevel can work on both namespaces and call stacks.

Did I get it right? I have yet to see a direct comparison between call stacks and namespaces, hence my question.

Otto Hunt
  • 107
  • 1
  • 7

1 Answers1

8

No, not quite. Namespaces and call frames are very different concepts. A namespace is a hierarchical structure of names that can disambiguate synonyms. You might have three variables named foo in your program, but they won't clash if you put them in different namespaces. Namespaces can be used both for variable and command names. Once created with namespace eval a namespace's contents are always accessible until you call namespace delete on it.

A call stack is a sequence of stack frames. The first stack frame, #0, always exists. Other stack frames are created whenever a command is called (this goes mostly for commands that are user-defined procedures, the "built-in" commands follow their own rules). They are destroyed again when the command returns. So if you call command A, and A calls command B, and B calls command C, you have a call stack that looks like this:

#3 : <C's variables>
#2 : <B's variables>
#1 : <A's variables>
#0 : <global and namespace variables>

Each stack frame is a scope in the sense that only the variables that are created there or imported into it can be accessed, unless you use upvar. Everything else is hidden. In most programming languages, names from an outside scope, such as the global scope, can be automatically accessed from an inner scope. Not so in Tcl.

Using upvar you can let a command look at things outside its own stack frame. C could, for instance, use upvar #0 foo bar to create an alias (bar) for the global variable foo, or use upvar 1 baz qux (note without a #) to create an alias (qux) for the variable baz in B's stack frame.

The uplevel command can be used along the same lines to execute a script in another stack frame, including the global one. During the execution, the script can access everything that is in that stack frame, but nothing else, including the variables in the stack frame that uplevel was called from.

C can also create an alias for the namespace variable ::abc::def using upvar #0 ::abc::def ghi, but don't do that, use namespace upvar ::abc def ghi instead.

Instead of upvar #0 foo foo you can use global foo to import a global variable. Inside a command defined in a namespace, the variable command can import variables defined in the same namespace.

It is often useful to upvar or uplevel into #0 (the global frame) or 1 (the caller's frame). Using other frame numbers is error-prone and usually an indication of poor design. The invocation upvar 0 foo bar creates an alias (bar) for a variable (foo) in the same stack frame, which can be surprisingly useful.

Commands that are called by events being processed execute outside the call stack, using the global level. There is no way for them to reach inside the active stack frames and access variables that reside there.

A simple demonstration:

namespace eval ::abc {
    variable def 42

    proc xyz {} {
        variable def
    }
}

set foo 1138

proc A {} {
    B
}

proc B {} {
    set baz 1337
    C
}

proc C {} {
    upvar #0 foo bar
    puts $bar
    upvar 1 baz qux
    puts $qux
    namespace upvar ::abc def ghi
    puts $ghi
}
Peter Lewerin
  • 13,140
  • 1
  • 24
  • 27
  • Only some commands push stack frames. The ones everyone knows about are procedure calls and `namespace eval`. (There are a few others, especially in TclOO…) – Donal Fellows Jan 15 '15 at 19:43
  • Thanks, @Hoodiecrow, you filled in some details. Much googling did reveal to me that namespaces are different than call frames. And that namespaces are optional and, perhaps, best avoided in small scripts (but useful for code reuse). Also call frames are created whenever a procedure is called. To clarify further: Did I state anything untrue in my question? – Otto Hunt Jan 16 '15 at 00:10
  • 1
    @user901750: there are some problematic statements. You are saying that a stack frame is sometimes called a call stack. This is wrong, but I suppose it was a mistake? The `proc` command does not create stack frames (but the commands it creates do, when called). A namespace does not change the Tcl naming context, but `namespace eval` does (by creating a new stack frame). `upvar` and `uplevel` do not work on (manipulate) namespaces as such, but can access namespace variables just like any other command that handles variables can. – Peter Lewerin Jan 16 '15 at 08:50