Version 19 of Dereferencing

Updated 2012-09-24 14:30:08 by LkpPo

Larry Smith See also deref for a package that handles dereferencing in an orthogonal and graceful way.

Richard Suchenwirth 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, references are strings (everything is a string), namely names of variables, which via a hash table can be resolved (dereferenced) to the "other something" they point to:

 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 with nested set commands. Compare the following C and Tcl programs, that do the same (trivial) job, 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 sets 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.

The hashtable for variable names is either global, for code evaluated in that scope, or local to a proc. You can still "import" references to variables in scopes that are "higher" in the call stack, with the upvar and global commands. (The latter being automatic in C, given the names are unique; else, the innermost scope wins).

(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 second 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...


See also "An Essay on Tcl Dereferencing" [L1 ] and "how do i do $$var - double dereferencing a variable".


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 offers this largely-working method of dereferencing on Feb 2nd 2006. 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 [foo]. (It works by overloading proc, and thus anything run outside of a proc 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 ;)