Dereferencing

See Also

deref
Larry Smith: A package that handles dereferencing in an orthogonal and graceful way.
handle
An Essay on Tcl Dereferencing
how do i do $$var - double dereferencing a variable

Description

Richard Suchenwirth (et al) 2002-11-14:

A reference is something that refers, or points, to another something (if you pardon the scientific expression). In C, references are done with *pointers* (memory addresses); in Tcl, handles are used instead. The name of a variable is used to locate (dereference) a value:

puts foo       ;# just the string foo
puts $foo      ;# dereference variable with name of foo
puts [set foo] ;# the same

This can be done more than one time by nesting set. Compare the following C and Tcl programs, that perform the same trivial task, and exhibit remarkable similarity:

#include <stdio.h>
int main(void) {
    int    i =      42;
    int *  ip =     &i;
    int ** ipp =   &ip;
    int ***ippp = &ipp;
    printf("hello, %d\n", ***ippp);
    return 0;
}

...and Tcl:

set i    42
set ip   i
set ipp  ip
set ippp ipp
puts "hello, [set [set [set [set ippp]]]]"

The C asterisks correlate to set calls in derefencing, while in Tcl similar markup is not needed in declaring. But why four set evaluations for three asterisks? Because also the first mention of i in C is a dereference, to pass its value into printf; Tcl makes all four of them explicit (otherwise, you'd see hello, i). One dereference is so frequent that it is typically abbreviated with $varname, e.g.

puts "hello, [set [set [set $ippp]]]"

has set where C uses asterisks, and $ for the last (default) dereference.

In Tcl, procedures and namespaces each get their own table of variables. namespace import, upvar, and global allow you to link variable references to variables in command evalutions that are "higher" in the call stack, and also to variables namespaces. In C, variables in an enclosing block are automatically available in enclosed blocks, unless they are eclipsed by variable declarations in the enclosed blocks.

(This little piece was written to defuse the superstition that Tcl has no references, or needs more of the same. See also Pass by reference, which is also called "call by name", pretty exactly describes how Tcl implements it...)


One visitor to the Tcl chatroom wondered why

expr $$item

turns the value of a variable whose name is in item from 530E001 to 5300.0 - what happens is that the Tcl parser does a first derefencing, e.g., if item has the value ID:

expr $ID

and expr does a kecond dereferencing (or substitution). Not much arithmetics to do on its input (which, interpreted as a number, looks like scientific notation), but at least you get it back in canonical double representation...


TV: Dereferencing could maybe be phrased as a form of variable substitution, unless I miss some major point of the page.. Interestingly, the ideas of functional (de-) composition probably can be thought to apply practically, which in tcl normally means that the evaluation levels of a nested level command are followed in the variable substitution.


MG 2006-02-02: Here is a largely-working method of dereferencing. It allows you to do, for instance...

set a "some real value"
set b ::a

proc foo {} {
    global a b;
    puts "Value: $$b"
}

, and get some real value as the result of evaluating foo. (It works by overloading proc, and thus anything run outside of a procedure won't have the dereferencing.) It's flaw is that it assumes (completely incorrectly) that only characters in the regexp range

[[:alnum:]_]

are valid for variable substituation after a $ - this is (I think) normally correct, but you can, of course, use things like

${some [long] name}
$some\ long \ name

But all it needs is a regexp pattern which does properly match all the valid ways to access a var through $, and it should work like a charm - if anyone knows a more accurate value, please alter the code. It will also work on an infinite number of $'s.

rename proc overload_proc
overload_proc proc {name args body} {
    while {[regexp {\$(\$+[[:alnum:]_]+)} $body]} {
        regsub -all {(\$(\$+[[:alnum:]_]+))} $body "\[set \\2\]" body
    }
    ::overload_proc $name $args $body
}
# and a small test
proc foobar {} {
    set real yay!
    set p1 real
    set p2 p1
    set p3 p2
    puts "$real / $$p1 / $$$p2 / $$$$p3"
}
foobar;# prints "yay! / yay! / yay! / yay!

(Yup, it basically just edits

$$$foo

out to

[set [set $foo]]

which is the "real" way to do nested variables/dereferencing. Check info body foobar after running the code+demo above, if you want to see the result.) (Oh, that does also mean that if you have, for instance,

set myvar {some text we $$foo don't want evaled or edited}

you'll end up with

set myvar {some text we [set $foo] don't want evaled or edited}

which is wrong. But this is just a 2am piece of example code ;)