1
votes

Which one is proper way of using variable with or without dollar sign? I thought that variable (without $) is used only during variable declaration (similar to Bash):

set var 10

In all other cases when variable is referred or used (but not declared) the proper syntax is $variable (with $):

set newVar $var
puts $var
puts $newVar

But then I found code where it is interchanged and seems that this code is working:

# using argv
if {[array exists argv]} {
  puts "argv IS ARRAY"
} else {
  puts "argv IS NOT AN ARRAY"
}

# using $argv
if {[array exists $argv]} {
  puts "\$argv IS ARRAY"
} else {
  puts "\$argv IS NOT AN ARRAY"
}

# using argv
if {[string is list argv]} {
  puts "argv IS LIST"
} else {
  puts "argv IS NOT LIST"
}

# using $argv    
if {[string is list $argv]} {
  puts "\$argv IS LIST"
} else {
  puts "\$argv IS NOT LIST"
}

Output:

argv IS NOT AN ARRAY
$argv IS NOT AN ARRAY
argv IS LIST
$argv IS LIST

Edit in reply to @glenn jackman:

Your reply pointed me to further research and I've found that TCL is capable doing some sort of "self modifying code" or whatever is correct name e.g.:

% set variableName "x"
x
% puts $x
can't read "x": no such variable
% set $variableName "abc"
abc
% puts $x
abc
% puts [set $variableName]
abc
%
%
%
%
%
%
% set x "def"
def
% puts $x
def
% puts [set $variableName]
def
%

Now your answer bring some light to problem, but one question remains. This is excerpt from documentation:

set varName ?value?
array exists arrayName

Documentation says that both functions expect variable name (not value) in other words it expect variable instead of $variable. So I assume (based on above self modifying code) that when I pass $variable instead of variable the variable substitution took place (exactly the same as code above). But what if $variable contains something that is not a list neither array (my arguments during testing was: param0 param1 "param 2" param3). From this point of view the output that says $argv IS LIST is wrong. What am I missing here?

Edit in reply to @schlenk:

Finally I (hope) understand the problematic. I've found great article about TCL, which explain (not just) this problematic. Let me pinpoint a few wise statement from this article:

  • In Tcl what a string represents is up to the command that's manipulating it.
  • Everything is a command in Tcl - as you can see there is no assignment operator.
  • if is a command, with two arguments.
  • The command name is not a special type but just a string.

Also following SO answer confirms this statement:

"In Tcl, values don't have a type... they question is whether they can be used as a given type." The command string is integer $a means:

  • "Can I use the value in $a as an integer"

NOT

  • "Is the value in $a an integer"

  • "Every integer is also a valid list (of one element)... so it can be used as either and both string is commands will return true (as will several others for an integer)."

I believe the same applies also for string is list command:

% set abc "asdasd"
asdasd
% string is list $abc
1
% string is alnum $abc
1

string is list returns 1 because $abc is string and also it is one element list etc. In most tutorials there are said that following snippet is the proper way of declaring and working with lists:

% set list1 { 1 2 3 }
% lindex $list1 end-1
2

But when everything in TCL is string the following is also working in my experience (if I am wrong correct me please).

% set list2 "1 2 3"
1 2 3
% lindex $list2 end-1
2
2
Read Tcl substitution rules (in man Tcl). You first get grouping of arguments, followed by substitution of variables and commands before you get the current command executed.schlenk
@schlenk I have done, several times, but I am still missing the point. SorryWakan Tanka

2 Answers

3
votes

It depends on the command. Some Tcl commands require a variable name as a parameter, if they need to modify the contents of the variable. Some are:

  • set
  • foreach
  • lappend
  • incr

Most but certainly not all commands want to take a variable's value.

You'll need to check the documentation for the relevant commands to see if the parameters include "varName" (or "dictionaryVariable"), or if the parameters are named as "string", "list", etc


An example using info exists which takes a varName argument:

% set argv {foo bar baz}
foo bar baz
% info exists argv              ;# yes the variable "argv" exists
1
% info exists $argv             ;# no variable named "foo bar baz"
0
% set {foo bar baz} "a value"   ;# create a variable named "foo bar baz"
a value
% info exists $argv             ;# so now that variable exists
1
2
votes

The important thing to know is that $x in Tcl is just syntactical sugar for the command set x. So you can translate any $x in Tcl code into [set x] in the same place to see what really happens.

The other important thing to consider is immutable values. Tcl values are immutable, so you cannot change them. You can just create a new changed value. But you can change the value stored inside a variable.

This is where the difference between commands taking a variable name and those that take a value comes in. If a command wants to change the value stored in a variable, it takes a variable name. Examples are lappend, lset, append and so on. Other commands return a new value and take a value as argument, examples include lsort, lsearch, lindex.

Another important point is the fact that you do not really have a list type. You have strings that look like lists. So that is what Tcl's string is list tests. This has some consequences, e.g. you cannot always decide if you have a string literal or a one item list, as it is often the same. Example given:

% set maybe_list a
% string is list $maybe_list
1

Combine that with Tcls nearly unrestricted names for variables, as already demonstracted by Glenn and you can get really confused. For example, these are all valid Tcl variable names, you just cannot use all of them with the $ shortcut:

% set "" 1       ;# variable name is the empty string
1
% puts [set ""]
% set " " 1      ;# variable name is just whitespace
1
% puts [set " "]
1
% set {this is a list as variable name} 1 ;# a variable with a list name
1
% puts [set {this is a list as variable name}]
1
% set Δx 1 
1
% incr Δx
2
% puts [set Δx]
2