When you use the syntax $ar($key)
, you are looking up the key $key
in the array ar
and returning its value. This is how Tcl is defined to work, it's in the basic language syntax. However, you're using the ar
variable to hold a scalar value, not an array (the two are completely separate; arrays aren't values, though lists and dictionaries are). That's why you're getting the error message.
To read from an array that is named in a variable, you either need to use a longer piece of syntax so that you substitute the variable name and then read from that variable (Tcl doesn't do this for you by default, since it's quite dangerous if you're not prepared for it) or you need to make an alias to the named array variable.
Double-Substitution via set
set d1 [set ${ar}($key)]
This works because $…
is really (under the hood) an alias for set
with a single argument. (Well, except that it doesn't actually call the command; they both call the same C API.) We use the ${...}
form to limit what the initial $
uses as its variable name. Be aware that if you put an array element name in ar
, you'll get odd results from this.
Aliasing an Array or an Element
upvar 0 $ar theAlias
set d1 $theAlias($key)
The upvar
command links variables together, and in particular when used with 0
as its first argument, it aliases variables in the current scope. By establishing theAlias
as a fixed alias to the actual array (the one named by $ar
), we can then access it just like a normal array. You could also alias directly to an element:
upvar 0 ${ar}($key) theAlias
set d1 $theAlias
Note the same syntax as used with the set
solution above; we want the name of the element, not to read it. (Warning: do not alias to elements of the global env
array; the code that couples to the system environment variables does not work in a friendly way with aliases.)
The main issue with using upvar
is that you can't turn theAlias
back into a non-aliased variable (though you can retarget the alias by calling upvar
again) other than by throwing away the current stack frame (trivial for a procedure body, not too hard for a namespace via namespace delete
, but problematic with the global namespace as deleting that terminates the whole Tcl interpreter).