Shadow Variables

RHS 12August2004

A package for treating parts of one variable as a whole other variable. More description to come later, but the basic idea (via some of the tests) is:

 test list.stackLevels-1.1 {
    Passing the name of a child into a proc to be upvar'd\
        will still modify the parent
 } -setup {
    reset main child
 } -body {
    proc modify {&var} {
        upvar 1 ${&var} var
        set var [string toupper $var]
    }
    set main {a b {c d} e}
    shadow child main 2
    modify child
    set main
 } -result {a b {C D} e}

 test array.stackLevels-1.1 {
    Pass the child into a proc, and change it via upvar
 } -setup {
    reset main child
 } -body {
    proc modify {&var} {
        upvar 1 ${&var} var
        foreach {key value} [array get var] {
            set var($key) [expr {$value * 20}]
        }
    }
    array set main {a 1 b 2 c 3}
    shadow child main
    modify child
    getArraySorted main
 } -result {a 20 b 40 c 60}

 test array.element-1.1 {
    Shadow an array element, parent follows the 'array get'\
        value of the child
 } -setup {
    reset main child
 } -body {
    array set main {a {A 1} b {B 2}}
    shadow child main b
    set child(B) [expr {$child(B) * 2}]
    getArraySorted main
 } -result {a {A 1} b {B 4}}

Feel free to take a look at the code for it at http://robert.rkseeger.net/tcl/shadow/shadow.tar.gz for now. I'll write up more information on what I was trying to do, and the limits of that approach, some other time.


RS 2004-08-17: Here's a very simple version that works only on lists, by linking a scalar variable to an element of the given list, and updating the list when the variable is changed (or re-reading the element from the list when the variable is read):

proc llink {listVar index linkVar} {
    upvar 1 $listVar list $linkVar link
    set link [lindex $list $index]
    trace add variable link {read write} [list llink'rw $listVar $index]
}
proc llink'rw {listVar index name el op} {
    upvar 1 $listVar list $name link
    switch -- $op {
       read {set link [lindex $list $index]}
       write {set list [lreplace $list $index $index $link]}
    }
}

# Testing:

 % set list {a b c}
 a b c
 % llink list 2 link
 % set link hello
 hello
 % set list
 a b hello
 % llink list 1 link2
 % set link2 world
 world
 % set list
 a world hello
 % set list {d e f} ;# testing the other direction too
 d e f
 % set link2
 e
 % set link
 f

RHS Indeed, thats pretty much what the code I wrote does, but it handles fringe conditions a little better. For example:

 % set list {a b c}
 a b c
 % llink list 2 link
 % proc bob {var} { upvar $var myvar ; set myvar uhoh }
 % bob link
 can't set "myvar": can't read "list": no such variable

And

 % set list {a b c}
 a b c
 % llink list 2 link
 % unset list
 % set link goodbye
 can't set "link": can't read "list": no such variable

I wanted to make sure my code would work in those situations, as well as be able to link variables to array elements.