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.
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
}
Related
New to tcl and trying to understand the "trace add variable" command.
I understand the need to invoke a callback function when a variable is "written" to.
But what is the use of the "read" option? For debugging?
One example use might be a global counter:
proc init { } {
set ::globalcounter 0
trace add variable ::globalcounter read ::gcountUpdate
}
proc gcountUpdate { } {
incr ::globalcounter
}
proc main { } {
init
puts $::globalcounter
puts $::globalcounter
}
main
I'm sure there are other uses. As you pointed out, debugging.
It could be used for enforcement of variable access by specific procedures.
One of the uses for read callbacks (which are indeed quite a bit less common than write callbacks) is in linking a Tcl variable to a C variable; the read callback is used to enforce the reading of the C variable and synchronizing the Tcl variable to it. (The write callback would ensure that the update of the Tcl variable gets reflected to the C variable.) Tcl's got a built-in C API that uses this mechanism, though it's using the underlying C API for variable traces rather than the Tcl API that is built on top of it.
You could also use a read callback to make an apparently read-only variable:
trace add variable foo read {apply {args {
upvar "#0" foo v
set v "definitely this"
}}}
puts $foo
set foo "that"
puts $foo
I don't recommend using variable traces on local variables. They have a lot more overhead (and internal complexity) than for global/namespace variables.
I wonder if it is right semantics to have a variable as an argument, something like this:
proc p1 {$aa} {}
I tried it on tclsh, there is no complaint, but the following experiment fails:
% set aa bb
bb
% set bb 200
200
% proc p1 {$aa} {puts $bb}
% p1 bb
can't read "bb": no such variable
Do you see what is wrong?
[UPDATE - after seeing Peter's answer]
I know the upvar semantic, thanks.
My main curiosity is still around using variable as proc argument. I know it is not common, but just cannot help musing what it really can do if the language syntax allows it.
Yes, your upvar example is exactly what I want to explore using a variable as proc argument, but my exploration so far tells me, really, there is no way we can do this because "$" is interpreted as a plain char.
Do debunk me please if I am wrong.
Tcl does not support reference arguments as such: the usual pass-by-reference semantics is too static for Tcl. Instead, the logic of the command can, by use of upvar, dynamically create reference parameters including indirect reference parameters and calculated reference parameters, and also retarget the local name to another external variable. The upvar mechanism may look ungainly, but is very powerful indeed.
(The (edited) remains of my original answer follows:)
The usual idiom for doing this is
proc p varName {
upvar 1 $varName var
puts $var
}
The upvar command looks into another stack frame (in this case 1, which is the caller's stack frame) and makes a variable named $varName (i.e. the variable's name is the value of varName) in that stack frame and a variable named "var" in the command's stack frame refer to the same data object.
I won't explain this further since this is not useful to the asker.
Documentation: proc, puts, upvar
There are cases where it makes sense for the arguments in a procedure creation call to be supplied from a variable. The main example is where you are creating procedures dynamically, calculating the arguments you want to use as you go along.
That's actually a use-case that isn't done very frequently! It's not particularly easy to use well. But I have done it. (OK, that's a method call, but the syntax of formal arguments is shared.)
The main reason that the capability is there is that it's part of Tcl's general syntax. Tcl tries very hard to not have special cases in how it parses things (other than in how a command parses the strings passed into it) and this includes in things that would be very special cases in the enormous majority of other programming languages, such as formal parameter lists to procedures. In Tcl, these are just ordinary values and can be produced using any technique that gives ordinary values.
The usual thing with putting them in braces is just how you do it reliably and is easy to teach. It's also overwhelmingly what people want to do.
All this is independent of the facts that Tcl's commands (including its procedures) can handle variable numbers of arguments (check out the special args parameter and how to specify default values) and that $aa is a legal (but strange) name for a local variable.
Sorry for the title couldn't think of anything better.
Heres my question:
Im trying to change a variable inside a proc only when the user hits space. the proc loops itself with after so if the user wish to hit space more then once the variable will increment.
Heres what I know:
There are many ways to go about this. You can pass variable inside the proc, you can link the variable with global or upvar and/or if your in a namespace then you can use variable. but the only one that seem to work with my is global. I'm getting a feeling it's because global makes a link but if thats true then variable should work too, right?
Here my test code:
proc test1 {} {
global testing
bind . <Key-a> {incr testing}
puts $testing
puts "test2"
after 100 test2
}
namespace eval test2 {
variable testing 0
namespace export {[a-z]*}
proc of1 {} {
variable testing
bind . <Key-a> {incr testing}
puts $testing
after 100 test3::of1
}
}
proc test3 {testing} {
bind . <Key-a> {incr testing}
puts $testing
puts "test4"
after 100 test4 $testing
}
set testing 0
#test1
test2::of1
#test3 0
grid .c
side question:
Why is it that in the global namespace we use set and global while in namespace we use variable (that seem to set and do global in one command). they seem to do the same job in different namespaces?
Variables in Callbacks
The scripts registered by the bind command — also in things like after events and fileevent callbacks — are evaluated in the global scope because they may be called long after the procedure that defined them returns; Tcl does not do scope capture (and that's actually a really complicated feature, so it isn't likely to come soon unless someone writes lots of code). This means that the variable that you want your procedure to notice changes to must also have global scope.
However, namespaced variables count just fine as global variables for the purpose of this discussion as they're nameable from a global context (real local variables are not). That means that we can do several ways to build a script that accesses a namespace variable from a bind-defined callback. Here's one of the nicer ones:
bind . <Key-a> [namespace code {incr testing}]
That's effectively the same as this:
bind . <Key-a> [list namespace eval [namespace current] {incr testing}]
(There are some strict differences that don't matter in this example.)
Another way to do the callback is this:
bind . <Key-a> [list incr [namespace which -variable testing]]
Which in this case is going to be much like:
bind . <Key-a> [list incr [namespace current]::testing]
If things are getting any more complicated than this toy example, it's time to stop updating variables directly in a binding script and instead write a helper procedure. That always simplifies things a lot. Or to use a class/object to encapsulate the details.
The variable Command: Why and Where to Use It
Why is it that in the global namespace we use set and global while in namespace we use variable (that seem to set and do global in one command). they seem to do the same job in different namespaces?
That's a good question. In fact, what global does is very much like upvar #0 (with the variable names doubled up), and set is a fundamental variable access command. They're commands that you can use regularly wherever you want their behaviour.
The variable command is rather stranger. What it does is three-fold:
If called in a namespace context and the variable does not exist in that namespace, it creates that variable in a present-but-unset state.
If called in a context with local variables, it links a local variable with the name (after stripping everything up to the last namespace separator) to a namespace variable with the name (using the whole supplied name if there are qualifiers, and resolving non-absolute names with respect to the current context namespace). This also forces the namespace variable to exist in the present-but-unset state.
If a value is given, the namespace variable is set to the value. This gets rid of the present-but-unset-ness.
The important behaviour is actually the creating of that present-but-unset state, since otherwise you can end up with a set (or array set) in the namespace escaping that namespace and instead working with a global variable, but not always. It all depends on the exact behaviour of the code that resolves variables, and that's deeply tricky. This is hard to explain properly, and ridiculously hard to justify. It's been the cause of quite a few outright bugs, and is definitely no better than a horrible misfeature.
The setting of the initial value is little more than a lollipop; you could instead put set straight afterwards without ill-effect. What's more, it inhibits using variable to pull in multiple variables from a namespace except in the case where you're setting them to a known value; great for initialisation, but sucky for other uses. (In case you hadn't guessed, I think this is an area of Tcl where the interface was got rather badly wrong back when it was introduced, back in Tcl 8.0. Not one bit of this is nice.)
The key take-away is this though:
Always use variable in your namespaces to declare variables as this is the only way you can be sure that the semantics is predictable. You can then initialise them any way you want. (You have to do it this way if you're creating arrays.)
Fully-qualified variable names have no craziness associated with them. Tcl always knows exactly how to look up the thing you're naming in that case.
I have several functions defined in namespace "b" which I export. I then import these functions to namespace ::x::Y, thusly:
namespace eval ::x::y "namespace import fun"
some time later I do:
namespace eval ::x::y fun
Where fun does:
proc fun {} {
puts "[namespace current]"
uplevel {puts "[namespace current]"}
}
What is printed is:
::b
::x::y
What I want and need is for 'fun' to happen in ::x::y and not in ::b. What am I doing wrong?
That's not how Tcl's namespaces work. Each procedure is associated with exactly one namespace, which is the one in which its name is located. When you use namespace import, an alias to the procedure is placed in the importing namespace that allows the procedure to be invoked from that other namespace, but the procedure itself remains in its original namespace and executes in that one.
If you want to know the caller's namespace, use uplevel namespace current (or uplevel 1 {namespace current} for a slightly windier but more efficient version). This doesn't actually tell you what namespace contained the command that was used to invoke the procedure though; for that, you need this monstrosity (in the invoked command):
namespace qualifiers [uplevel 1 [list namespace which [lindex [info level 0] 0]]]
Of course, if you're needing that a lot then you're probably doing something wrong. (That's obvious, given the length and complexity of code required to get the information.)
In particular, if you're pretending to do object orientation with this, please stop and use a real object system that gets all the tricky details right. Tcl 8.6.0 includes one (two, if you've got the contributed extensions), and there are many for older versions available as extension packages.
So, I am testing somethings out, and have a "test" proc like so:
proc test {arg} {
global state
puts "Your arg is: $arg"
set state 1
}
test somearg
vwait state
From reading about uplevel and upvar, is there a way that I can get away with not having to use global, and use either one of those options to set the state to "1" and then exit the program?
Yes, except that vwait always uses global variables for waiting on (strictly, it resolves variable names in the global scope; you can use other namespaces if you provide qualified names). What you can't do is wait on a local variable (because events can't see local variables outside their own call stack). Maybe this will change in the future, but certainly not now.
In relation to the question about global, these statements are all the same in effect inside a procedure:
global foo
variable ::foo
upvar #0 foo foo
upvar ::foo foo
namespace upvar :: foo foo
You also have a bug in your code: you set the state before waiting for it to change. That won't work anyway because you've got to wait first, and set the state from within some kind of event.
You're asking for two different things. First, about the variable. You can use upvar like so:
proc test {arg} {
upvar #0 state state
puts "Your arg is: $arg"
set state 1
}
Or, more easily, you can just use the namespace qualified name:
proc test {arg} {
puts "Your arg is: $arg"
set ::state 1
}
The second "half" of your question is some odd usage of vwait. I just want to point out that your snippet of code won't complete because you're waiting for the variable 'state' to change, but there's no event that will ever change state. You already changed it when you called test. So, unless you've set up a window/button or something that might cause state's state to change, your script will hang.
It's worth reading the wiki and the man page for vwait.