'''[http://www.tcl.tk/man/tcl/TclCmd/upvar.htm%|%upvar]''', a [Tcl Commands%|%built-in] [Tcl] command, makes a [variable] at the current Commands%|%built-in] [Tcl] command, links a [variable] to some other variable at the specified [uplevel%|%level] of evaluation. ** See Also ** [variable]: like upvar, links [namespace] variables, but not variables at other levels of evaluation, into the current [uplevel%|%level] of evaluation [namespace upvar]: like `[upvar]` but links variables into a namespace rather than into the current call frame. [http://dqd.com/~mayoff/notes/tcl/upvar.html%|%Guidelines for using upvar and uplevel], by Rob Mayof: the result of an exchange on the [AOLserver] mailing list. Some of the information at the mentioned site seems to be incorrect. Especially in reference to `[uplevel]`. I didn't look at in great detail however. Does this advice simply reflect best practice or what can be done? [http://dqd.com/~mayoff/notes/tcl/upvar.html%|%Guidelines for using upvar and uplevel] ,by Rob Mayof: the result of an exchange on the [AOLserver] mailing list. Some of the information at the mentioned site seems to be incorrect. Especially in reference to `[uplevel]`. I didn't look at in great detail however. Does this advice simply reflect best practice or what can be done? `[uplevel]`: Evaluate scripts at a specified level of execution. ** Synopsis ** : '''upvar''' ''?level? otherVar myVar ?otherVar myVar ...?'' ** Documentation ** [http://www.tcl.tk/man/tcl/TclCmd/upvar.htm%|%official reference]: ** Description ** `upvar` makes ''myVar'' an alias for ''otherVar''. ''otherVar'' can `upvar` links ''otherVar'' to ''myVar'', after which they are effectively the same [variable], and performing an operation such as `[set]` or `[unset]` on one is equivalent to performing the operation on the other. ''otherVar'' can enclosing level. ''otherVar'' can include [namespace qualifiers], in which case it is resolved relative to the [namespace current%|%current namespace] at the level of evaluation specified by ''level''. When `upvar` is called from the body of a command, ''myVar'' is a variable in the caller of `upvar`, and may not contain namespace qualifiers. Otherwise, ''myVar'' is resolved relative to the [namespace current%|%current namespace]. `upvar` returns an empty string. It is an error for ''myVar and ''otherVar'' to resolve to the same name unless defaults to `1`. ''myVar'' must not exist at the time when `upvar` is invoked. The variable named by ''otherVar'' need not exist at the time of the call. `upvar` never interprets ''myVar'' as an array element. Even if the ''myVar'' looks like an array element, e.g. '''`a(b)`''', a regular variable is created. ''otherVar'' may refer to a scalar variable, an array, or an array element. `upvar` makes it more convenient to work with variables at other levels of evaluation, and facilitates Tcl procedures that provide [new control `upvar` makes it more convenient to work with variables at other levels of evalution, and facilitates Tcl procedures that provide [new control `[variable]` overlap in functionality. `[namespace upvar]` doesn't add any functionality over `upvar`, but its syntax, along with the accompanying difference in variable resolution makes, it more convenient in most cases for linking namespace variables. A script should not usually use the literal names of variables of its callers. `[upvar]` is one of the mechanisms that can be used to avoid such a situation. `[upvar]` creates ''myVar'', but does not create ''otherVar'': ====== namespace eval n1 { proc p1 {} { n2::p2 puts "p1 vars: [info vars]" ;# -> p1 vars: } } namespace eval n2 { proc p2 {} { upvar p1var p2var puts "p2 vars: [info vars]" ;# -> p2 vars: p2var } } n1::p1 ====== ** Examples ** To set a variable in the caller: ====== proc someproc varname { upvar 1 $varname var set var 5 } someproc a set a ;# -> 5 ====== ** Link to a `[global]` variable ** ====== upvar #0 foo foo ;# equivalent to: global foo ====== [MS] notes that the equivalence to `[global]` is '''only within proc bodies''': `[global]` is a no-op outside of them, '''`upvar #0`''' is not. ** Link to another variable in the same execution frame ** ====== upvar 0 foo bar ;# assigns alias 'bar' to a local variable 'foo' ====== ''otherVar'' can be unqualified, semi-qualified, or fully qualified. In all cases, the variable is resolved at the indicated level according to normal rules. One trick is to use `[upvar] 0` to link to another variable in the same call frame, or to a variable in some namespace: ====== upvar 0 ::path::to::variable myvarname ====== ** Using `[upvar]` with an array ** ====== # print elements of an array proc show_array arrayName { upvar $arrayName myArray foreach element [array names myArray] { puts stdout "${arrayName}($element) = $myArray($element)" } } proc main {} { set arval(0) zero set arval(1) one show_array arval } ====== In the example above, any changes made to elements in `$myArray` will be reflected in `$arval`. ** `upvar` prefix* for Variables in an [Array] ** Art M.: ====== # Purpose - Utility to turn an subset of an array into alias upvar'd variables # for local function use. # This assumes the array is defined in one level above where this routine is # called. # Todo - # allow array to be defined at other levels. # why does it not work unless I quote ${arName}(ind) with {} ?? # # Right now the source array has to be in the global level # proc upAr {arName index} { upvar $arName ar foreach ind [array names ar ${index}*] { regsub $index $ind {} indVar if {[info level] == 1} { # This was executed from level 0 uplevel 1 [list upvar #0 ${arName}($ind) $indVar] } else { uplevel 1 [list upvar #0 ${arName}($ind) $indVar] } } } ====== ** Detecting a variable created by `upvar` ** [aspect] 2014-08-08, on the `[trace]` page, illustrated one difference between variables created by `upvar` and other variables: ====== % unset -noc x % set y 1; set z 1; % upvar 0 y x % upvar 0 z x ;# reupvar - no problems % upvar 0 x y # ERROR: variable y already exists ====== [PYK] 2014-08-18: Drawing from the previous example, here is a non-destructive way to determine whether a variable was created by `upvar`, returning `1` if it was: ====== expr {![catch {upvar 0 varname varname}]} ====== One thing I still see no way to accomplish is to determine the target of a variable created by `upvar`. Perhaaps `upvar` could be modified to return the target of an upvar'ed variable when it is given only one argument. [aspect]: one (very cumbersome) way is to use traces: a trace is visible on all `upvar`ed links and can be added without side effects, which provides a simple way to test if two variables are aliases. That reduces the problem to enumerating every variable that could be an alias -- which I think is only complicated by coroutines, needing [coroinject] to inspect properly. ** Historical Gotcha: ''otherVar'' That Looks Like a Number ** ** Gotcha: ''otherVar'' That Looks Like a Number ** [PYK] 2017-02-09: Unless one is interested in the history, this section is safe to [AMG]: I like that ''level'' is optional, but I don't like the method used to ====== proc naive_iplsort {lst} { upvar $lst var set var [lsort $var] } ====== breaks whenever `$lst` starts with `#` or a numeral, because `[upvar]` guesses that its first parameter is ''level'' rather than ''otherVar''. ======none % set data {4 3 2 1 0}; set #0 {9 8 7 6 5}; set 0a {e d c b a} % naive_iplsort data 0 1 2 3 4 % naive_iplsort #0 wrong # args: should be "upvar ?level? otherVar localVar ?otherVar localVar ...?" % naive_iplsort 0a expected integer but got "0a" ====== The fix is to explicitly supply ''level'': ====== proc paranoid_iplsort {lst} { upvar 1 $lst var set var [lsort $var] } ====== This makes it abundantly clear to Tcl that `$lst` is an not ''level'' but an ''otherVar'' argument, but I think it's kind of painful to type, since it admits that Tcl has the feature that ''level'' is optional, but that feature can '''never''' be used safely. At least it works: ======none % set data {4 3 2 1 0}; set #0 {9 8 7 6 5}; set 0a {e d c b a} % paranoid_iplsort data 0 1 2 3 4 % paranoid_iplsort #0 5 6 7 8 9 % paranoid_iplsort 0a a b c d e ====== I know it may be a bit too late to change this, but I'd prefer that `[upvar]` use a different algorithm to determine whether its first argument is ''level'' or ''otherVar''. I'll explain by way of demonstration: ====== rename upvar Upvar proc upvar {args} { if {[llength $args] == 0} { # Received zero arguments. # Allow [upvar] to raise an error. } elseif {[llength $args] % 2 == 0} { # Received an even number of arguments. # Conclude that the first is a variable name. # Prepend $args with the default level, 1. set args [linsert $args 0 1] } else { # Received an odd number of arguments. # Conclude that the first is a level number. } # Let the real [upvar] do its thing. uplevel 1 Upvar $args } ====== And a test: ====== % upvar 0 0a alpha % set 0a success; puts $alpha success % proc test {} {upvar 0a beta; puts $beta} % test success ====== This should also work in the case of multiple variable pairs. Ironically, I use `[uplevel] 1` in the above code. `[uplevel]` has a similar problem: "Level cannot be defaulted if the first command argument starts with a digit or #." But there's no possibility of a cutesy workaround wrapper because it can legally accept ''any'' number of arguments, whereas ''upvar'' can only take an even number (not counting ''level''). So perhaps for the sake of consistency with `[uplevel]` I should just go ahead and put those blasted 1's in there anyway. :^) ** Performance ** [IL]: I'm curious about the performance implications of `upvar` recently. Is there a page discussing them? Passing a `[list]` vs. upvaring it for example. I had always assumed that any pass in Tcl was by reference, unless upvar is exactly that and everything else is by value? [RS]: The Tcl way is indeed normally passing by value, which helps to make the code more robust (no side-effects possible). `upvar` is the way to pass by reference. The difference appears to be very little (or 50% more, if you take the Win XP timing to be precise): ====== proc a x { expr {$x*$x} } proc b _x { upvar 1 $_x x expr {$x*$x} } % time {set x 42; a $x} 100000 2 microseconds per iteration % time {set x 42; b x} 100000 3 microseconds per iteration ====== [PYK] 2014-08-14: Coming to Tcl from other language traditions, one of the things that took longest to sink in was how Tcl's [copy-on-write] semantics eliminate any performance penalty of its pass-by-value semantics in most cases. It is precisely because of [copy-on-write] that the normal pattern in Tcl is to pass the value to procedures that won't modify it, and to pass the name of the variable to procedures that will. This issue also illustrates that comparing Tcl to other [operating system%|%operating systems] is sometimes more apt than comparing it to other [programming language%|%programming languages]. [Lars H] 2008-08-21: When implementing the [http://en.wikipedia.org/wiki/Wikipedia_talk:Articles_for_creation/Submissions/Man_or_boy_test_implementations#Tcl%|%Man or Boy Test] in Tcl a while back, I was surprised to see that the implementation using `upvar` ====== proc A {k x1 x2 x3 x4 x5} { expr {$k<=0 ? [eval $x4]+[eval $x5] : [B \#[info level]]} } proc B {level} { upvar $level k k x1 x1 x2 x2 x3 x3 x4 x4 incr k -1 A $k [info level 0] $x1 $x2 $x3 $x4 } proc C {val} {return $val} interp recursionlimit {} 1157 A 10 {C 1} {C -1} {C -1} {C 1} {C 0} ====== was actually about 30% faster than the "purer" implementation passing as much as possible by value (and using `[uplevel]` for the side-effect required by the test): ====== proc AP {k x1 x2 x3 x4 x5} {expr {$k<=0 ? [eval $x4]+[eval $x5] : [BP \#[info level] $x1 $x2 $x3 $x4]}} proc BP {level x1 x2 x3 x4} {AP [uplevel $level {incr k -1}] [info level 0] $x1 $x2 $x3 $x4} proc C {val} {return $val} interp recursionlimit {} 1157 AP 10 {C 1} {C -1} {C -1} {C 1} {C 0} ====== This is probably due to dragging along more data in the latter case. (I doubt it is a [shimmering] effect, since failure to share [Tcl_Obj]s should lead to an exponential overhead.) ** Discussion ** From [http://www.psg.com/~joem/tcl/faq.html%|%Tcl Language Usage Questions And Answers], by Joseph Moss, I see: Use '''upvar''' rather than try to use '''[global]''' variables when possible. If the function is event driven, you are forced to use global variables. ''the statement "you are forced to use global variables" is a bit misleading. It's not that upvar doesn't work in event driven programs, it's just that functions called from events are run at the global level, rendering upvar relatively useless. If you have a callback that calls another proc, that second proc can use upvar with impunity'' ---- [ZB]: According to the documentation, : The variable named by ''otherVar'' need not exist at the time of the call; it will be created the first time ''myVar'' is referenced, just like an ordinary variable. OK, so let's try following short example, also taken from the docs (`proc add2 name { upvar $name x ; set x [[expr {$x + 2}]] }`): ======none % proc add2 varname {upvar $varname x; set x [expr {$x + 2}]} % add2 b can't read "x": no such variable % puts $b can't read "b": no such variable % ====== Isn't it contrary to that remark "the variable doesn't need to exist, it will be created"? `$b` hasn't been created. Pay attention, that variable myVar ("x" in example above) '''has been referenced'''. Although I agree, that it looks like: "take something non-existing, and add a value of 2 to this" - but still: variable x has been referenced. [PYK] 2014-03-24: A variable can be created but not defined. `upvar` creates the variable when it is referenced, but doesn't necessarily define it. `[info exists]` only returns `1` if the variable is both created and defined. ====== #shows that the variable is not created namespace which -variable ::greeting ;#-> {} proc myproc {varname} { upvar $varname var #reference the variable without defining it trace add variable var read {;#} } myproc greeting #the variable now exists namespace which -variable ::greeting ;#-> ::greeting #and yet, it doesn't :) info exists ::greeting ;#-> 0 ====== ---- '''[JR] 2014-11-14 21:09:19''': I often find it useful to pass a reference to a local variable to another command that will use it as part of a callback (such as htmlparse::parse) . However, without knowledge of the implementation I would have no way of knowing exactly how many stack frames are in between the use and call, so I pass the references as absolute frames. I use these two procs for the job: ====== proc localvar {v} { return [list #[uplevel info level] $v] } proc stackvar {rv v} { uplevel upvar {*}$rv $v } ====== The first gives a reference to a local variable, the second binds that reference to a local variable. ====== set text {} htmlparse::parse -cmd [list doit [localvar text]] $html proc doit {varinfo tag slash param text} { stackvar {*}$varinfo localvar set localvar ... } ====== ** History ** `upvar` was proposed around 1991 by [Karl Lehenbauer], who also wrote the