Version 39 of upvar

Updated 2014-03-25 02:05:51 by pooryorick

upvar , a 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.

See Also

upvar ?level? otherVar myVar ?otherVar myVar ...?

Documentation

official reference

Description

upvar makes myVar an alias for otherVar. otherVar can upvar simplifies the implementation of call-by-name procedure calling and also makes it easier to implement Tcl procedures that are new control constructs. namespace upvar, upvar, and variable overlap in functionality, but have different design goals. It is an error for myVar and otherVar to resolve to the same name unless upvar 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 uplevel, and if omitted, defaults to 1. For each otherVar, upvar makes a link in the current execution frame to the variable named by otherVar and 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, including set and unset, will be observable at the other level. myVar must not exist at the time when upvar is invoked. The variable named by 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 otherVar'' is typically a value passed in by the caller of the script where upvar occurs. It's usually ill-advised for the script where upvar occurs to otherwise "know" the names of variables in its callers. If it does, there's probably a design problem. upvar makes it more convenient to work with variables at other levels of evaluation, and facilitates Tcl procedures that provide [new control

Example: link to a global variable

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.



'''Example:   link to another variable in the same execution frame:'''
** Link to another variable in the same execution frame ** 

upvar 0 foo bar  ;# assigns alias 'bar' to a local variable 'foo'
upvar  0 foo bar  ;# assigns alias 'bar' to a local variable 'foo'

''otherVar'' can be unqualified, semi-qualified, or fully qualified.  In
''otherVar'' can be unqualified, partially-qualified, or fully qualified.  In
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 **
'''Example: 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)"
       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] **
** Example: `upvar` prefix* Array Elements **
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]  
        }
    }

}


** 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''.

% 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:
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:

% 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
[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}]] }`):

% 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
[PYK] 2014-03-24: Yes, that wording in th documentation is a little off.  What
it means is that after `$x` is linked to `$b`, creating `$x` is equivalent to
creating `$b`:

#shows that the variable is not created proc someproc {varname} {

    #reference the variable without defining it
    set var 5

myproc greeting someproc a set a ;# -> 5


See Also

variable
like upvar, links variables into the current execution frame or namespace, but functions a little differently
namespace upvar
like upvar but links variables into a namespace rather than into the current call frame.
Guidelines for using upvar and uplevel ,by Rob Mayof
the result of an exchange on the AOLserver mailing list resulted. 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?