This issue of dereferencing seems to come up frequently enough to deserve a page of tips, suggestions, warnings, etc. Tcl is documented to, and does, work as follows:
% set amount 42 42 % puts "I owe you $$amount" I owe you $42
The first dollar sign cannot be parsed as variable reference, and is left as is; the second resolves with $amount.
The basic situation is that a user is trying, either intentionally or accidentally, to do pointer like design. They want "$$amount" to mean the value of a variable, the name of which is inside another variable, that happens to be named "amount". As noted below, a very good essay on the subject is: http://phaseit.net/claird/comp.lang.tcl/tcl_deref.html
Larry Smith Tcl, as always, can be made to deal with references in a more graceful manner - see deref.
Whenever you're in trouble with the "$" symbol, it might help to remember that "$myvar" is no more than a shorthand way of writing "[set myvar]" (but it only kicks in when "myvar" is recognized as a variable reference). Just for debugging, try finding every "$" symbol which you think should cause a dereference, and replace it with "[set]". Thus, "$$var" becomes "[set [set var]]]" if you wanted "$$" to mean a double dereference, or "$[set var]" if you just wanted a dollar symbol prefixed to the result of [set var]. While this technique is too verbose to be used all the time, it can still be an easy way to clarify where dereferencing occurs.
Another approach is shown in this example:
set a "123" set b "3.1415" set c "The price of petrol in Peoria" foreach var {a b c} { puts [set $var] }
DKF: More often than not, a solution involving arrays produces a better (more robust, etc.) result:
set data(a) "123" set data(b) "3.1415" set data(c) "The price of petrol in Peoria" foreach var {a b c} { puts $data($var) }
Another possibility (more so when splitting things up into procedures) is to use upvar.
CL proposes that double-dereferencing is essentially always an error for application programming. Many, many of the people who ask about '$$' are coming from Perl, but, even there, such double-dereferencing is recognized as a hazardous error [L1 ]. See also "An essay on Tcl dereferencing" [L2 ].
GWM Another means of accessing the $$var is to write subst $$var. Then $var is substituted and the resulting ${whatever was in var} is evaluated. As other people are pointing out here, if the result of $var is not the name of another var (or even the same var) then error results.
set jo jim puts $$jo ;# returns "$jim" puts [subst $$jo] ;# returns "can't read "jim": no such variable" set fred jo puts [subst $$fred] ;# returns "jim"
Example of a var referring to itself:
set jo jo puts [subst $$jo] ;# again "jo"
Lars H: It should also be observed that the meaning of $ followed by a symbol (other than underscore, left brace, or left parenthesis) is undefined in the Endekalogue. This makes such constructions interesting candidates for future extensions in Tcl syntax; $$var itself is one of the suggestions for expanding a list. Conclusion? Escape all $ that shouldn't be substituted, just to be on the safe side in case this piece of syntactic real estate will be claimed by some future addition to the language.
TV It's probably worth a page by itself as a subject. A pointer in C language sense is not the same as a indirect reference. Also, associations are not the same as referencing something by name and that is not the same as a pointer in C, in normal language or in for instance a URL. And a function, as clearly the case in tcl is referenced by name, and can be stored as a list.
The simple answer I guess is to use eval:
set whichvar b set b 11 eval puts $$whichvar
Which leaves a lot of questions about quoting, and preventing the evaluator to perform substitutions (possible through escaping).
A pointer in C, which is rarely seriously useful in large repetition as in ****something, that would refer to datastructures and referencing them which can work for initializing accessing something in a tree when the structure contains the forward reference as first element, is not the same idea. A pointer in a digital signal processor could be pointing in a subspace of memory not made into the comfortable the_whole_memory_is_one_linear_list which also PC hearts eventually got around to (more or less..), which is like in computer design, where something is a pointer when it is used to point into a memory, which is just the same as an integer memory with access to some address bus.
Though of course in C one would refer to another variable through a pointer, which in various senses is also a variable:
main() { int i; int *pointer; i = 13; pointer = &i *pointer = 14; return *pointer;
}
In Tcl, we have symbolic behaviour of the tcl interpreter which of course at some level and point are implemented at C level (I think it all it, though I could be mistaking) which makes variable referencing by name in runtime different than the above C level.
A nice example is:
info var prefix_*
which lists all the variables with a certain prefix.
I just tried to list some functions associated with blocks on the bwise canvas:
(Tcl) 101 % foreach var [info var *bfunc] {eval puts \"$var $\{$var\}\"} mapping.bfunc: set mapping.newstate [fsm_state_map ${mapping.oldstate} ${mapping.input} ] ; set mapping.output [fsm_output_map ${mapping.oldstate} ${mapping.input} ] Entry1.bfunc: Proc1.bfunc: set Proc1.out ${Proc1.in} state.bfunc: set state.out ${state.in} Mon1.bfunc: .mw.c.mon1 del 0.0 end ;.mw.c.mon1 insert end "${Mon1.in} \n" ; .mw.c.mon1 see end
The messy looking (though I'm used to it) string like construction is for the aforementioned quoting and substitution issue. Variable names are considered to end with a dot (.) so I have to enclose them in braces to make them a list first.
Of course, that "messy string like construction" can also be written as
foreach var [info var *bfunc] {puts "$var [set $var]"}
Since this will be completely byte-compiled, it is faster (and safer) than the eval construction above.
Referencing commands through variables, and making that a programming habit has the clear risc that one may loose touch of what happens, but then again, using certain strings with the system command containing those wonderful Recursive options can be dangerous just the same. And it is not illegal in computers to do symbolic referencing, its just not common practice, because direct and relatively fast indirect referencing as we know it in C and other compiled (fast and formal) languages is based on numerical representation of uniquely defined memory locations containing the referenced data. A pointer is usually even literally an integer, though it is possible to have bit shift and obviously ranges involved.
To make one variable reference another, one would need to first resolve the name of the target variable to the memory location where it resides, by using an associative procedure, assuming such is possible, as it is with what we consider ordinary computers.
In tcl, referencing to a variable can be resolved in runtime with no special effort, and quite efficiently, and the syntax at least does not prevent one to use above kind of constuctions elaborately. The constructs themselves are not something computer science shouldn't be into, it was the stuff which also was in the so manieth generation computers (I think it was 5th) which was in attention a few decades ago.
Certain problems, which are nowadays often seen as to be solved by a database (which of course will often in fact be also nothing much more but another C program with extensive associativeness and a large swapping memory pool, often not used all too efficiently compated to specific and well made solutions), are associative in nature, and of language is a good example. How we could use good formalisms to prevent errors and 'cover' a good spanning set of facilities and constructs is an interesting question in my opinion.
TV (apr 16 03) Glancing over the page later, I thought about the need for the right quoting, such as where the name of the variable in the first indirection or the second contains characters which must be escaped, or when the name is a list:
set v1 "puts \$v2" set v2 v1 puts $$v2 $v1 eval puts $$v2 puts $v2
Now lets take a variable with a space in the name (not permitted in bwise blocknames because of the link with window names):
set v\ 1 $v1 set v2 v\ 1 eval puts $$v2 can't read "v": no such variable eval puts $\{$v2\} puts $v2
TV I don't know who put in the remark about the [set var] construct, but that looks practical, it could make a lot of my code neater, I'll try it. Using associative arrays is of course fine, but I don't think it always solves the problem, and of course it requires you to either start when you don't want to or at some point convert to an array, which takes up more memory, and even a lot when you have overlapping sets which map to many arrays only for the sake of referencing.
I can agree with the programming neatness remarks, but I guess that depends on where you stand: using lists as programs may not be what a pascal programmer wants, and hard to put in neat and decent diagrams, but a powerful construct to prevent the enormous amount of source and binary code when all choices and associations and their resulting program flow space are explicified at programming time. Like writing a tcl interpreter in tcl.
djconnel - 2017-08-08 21:32:28
If the variable evaluates to a numerical value one can also use expr, for example:
> set a 42 > set b a > puts [expr $$b] 42
AMG: This may work in this simple example, but it is slow and unsafe. Try this instead:
> set a 42 > set b a > puts [set $b] 42
aledquin - 2023-03-02 00:15:17
I have a few examples for double replacement, I hope it can help :)
set var_value "caramba" set var_name var_value [set var_name] ;# --> var_value [set $var_name] ;# --> caramba
The following example converts
proc convert_var_curv_braces_to_dict {{}} { upvar 1 [info locals] sv foreach ssv $sv { if [regexp {(\S+)\{(\S+)\}} $ssv full dictname keyname] { upvar 1 $full $full foreach dict_values [set $full] { lappend list_values $dict_values } set [set dictname]($keyname) $list_values unset list_values if {$dictname ni $sv} {lappend new_sv $dictname} } else { lappend new_sv $ssv } } return $new_sv }