'''[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, creates a link at the current level to a variable at the level of one of its callers. : '''upvar''' ''?level? otherVar myVar ?otherVar myVar ...?'' `upvar` makes ''myVar'' an alias for ''otherVar''. ''otherVar'' can This command arranges for one or more local variables in the current procedure to refer to variables in an enclosing procedure call or to global variables. ''level'' may have any of the forms permitted for the '''[uplevel]''' command, and if omitted, defaults to `1`. For each ''otherVar'', `upvar` makes a link in the current frame to the variable named ''otherVar'' located at the level specified by ''level''. ''myVar'' must not exist at the current level when `upvar` is invoked. The variable named by ''otherVar'' need not exist at the time of the call. After the link is made, any changes to the variable will be observable at the other level, including [set] and [unset]. It is an error for ''myVar and ''otherVar'' to resolve to the same name unless ''MyVar'' is always treated as the name of a variable, not an array element. Even if the name looks like an array element, such as '''a(b)''', a regular variable is created. ''OtherVar'' may refer to a scalar variable, an array, or an array element. '''Upvar''' returns an empty string. `upvar` never interprets ''myVar'' as an array element. Even if the ''myVar'' `upvar` simplifies the implementation of call-by-name procedure calling and also makes it easier to build new control constructs as Tcl procedures. `upvar` makes it more convenient to work with variables at other levels of evaluation, and facilitates Tcl procedures that provide [new control ** Link to a `[global]` variable ** ** Using upvar ** [MS] notes that the equivalence to `[global]` is '''only within proc bodies''': * *really* try to avoid using [upvar] with variable names not passed in by the caller * consider whether [namespace upvar] is a better fit ** Link to another variable in the same execution frame ** ''otherVar'' can be unqualified, semi-qualified, or fully qualified. In <> ====== [CMcC]: Could someone write about what happens if otherVar is a namespace-qualified name? Does a fully-qualified name work? What does a partially-qualified name do? [Lars H]: Nothing special, much; these variables are also looked up according to the normal rules. Fully qualified names are straightforward. Partially qualified names are relative to the namespace of the specified ''level'', but the tendency of partially qualified names to bind to existing variables that are relative to the :: namespace can produce surprising effects that may obscure this. ---- From http://www.psg.com/~joem/tcl/faq.html I see: ** Using `[upvar]` with an array ** 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'' # print elements of an array proc show_array arrayName { upvar $arrayName myArray foreach element [array names myArray] { puts stdout "${arrayName}($element) = $myArray($element)" puts stdout "${arrayName}($element) = $myArray($element)" } proc main {} { set arval(0) zero set arval(1) one show_array arval ====== ====== To return an array from a procedures, just take the array name in as an argument, as above. Any changes you make in the array will be made in the parent's array as well. Extended Tcl introduces a concept called keyed lists which are arrays made out of lists of key-value pairs and can be passed by value to routines, over networks, etc. ---- [RS] [upvar] can also be used for both global and local variables: upvar #0 foo foo ;# equivalent to: global foo upvar 0 foo bar ;# assigns alias 'bar' to a local variable 'foo' ====== In the example above, any changes made to elements in `$myArray` will be [MS] notes that the equivalence to [global] is '''only within proc bodies''': [global] is a no-op outside of them, '''upvar #0''' is not. ---- A recent exchange on the [AOLserver] mailing list resulted in a page with guidelines for using upvar and [uplevel] being published at http://dqd.com/~mayoff/notes/tcl/upvar.html . ** `upvar` prefix* for Variables in an [Array] ** ---- Art M.: 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? ====== ---- [Art M.] # Purpose - Utility to turn an subset of an array into alias upvar'd variables # 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. # 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} { proc upAr { arName index } { foreach ind [array names ar ${index}*] { foreach ind [array names ar ${index}*] { if {[info level] == 1} { if { [info level ] == 1 } { # This was executed from level 0 } else { uplevel 1 [list upvar #0 ${arName}($ind) $indVar] } } } ====== ---- [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 identify whether or not it is present. The following code (an in-place lsort): ====== proc naive_iplsort {lst} { upvar $lst var set var [lsort $var] } ====== breaks whenever `$lst` starts with `#` or a numeral, because `[upvar]` guesses breaks whenever $lst starts with # or a numeral, since [[upvar]] guesses that its first parameter is ''level'' rather than ''otherVar''. ======none ====== % 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'': The fix is 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: This makes it abundantly clear to Tcl that $lst is a variable name and not a level. But I think it's kind of painful to type, since it admits that Tcl has a feature (''level'' is optional) that 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]` 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 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 So perhaps for the sake of consistency with [[uplevel]] I should just go ahead and put those blasted 1's in there anyway. :^) ---- [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): ** Performance ** 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 [Lars H], 2008-08-21: When implementing the ''Man or Boy test'' [http://en.wikipedia.org/wiki/Wikipedia_talk:Articles_for_creation/Submissions/Man_or_boy_test_implementations#Tcl] 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 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 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.) [ZB] In the docs upvar page there is a remark: "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}]] }"): [ZB]: According to the documentation, ====== % proc add2 name { upvar $name x ; set x [expr {$x + 2}] } 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 Isn't it contrary to that remark "the variable doesn't need to exist, it'll be created"? The "b" variable 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 <> ======