if {[expr {$a < 0}]} {set a 0}Such silly sort of code lines may well occur when a beginner is getting confused about using the proper quoting, or when one thinks that only simple comparisons are allowed with if { } ....The more appropriate method of writing the above is
if {$a < 0} {set a 0} ;# the first 'if' argument already expects an expressionWhile the original line may be valid Tcl code, it is not one of the Tcl best practices. This is the importance of learning Tcl idioms and having a collection of good Tcl examples from which to learn.To show a possible source of confusion, two commands are compared here:
if {[command...] == xx} { for {[command...]} {..} {For a confused beginner, both lines seem to conform to a consistent syntax. Of course, the second case yields an error when the nested script does not return a valid command name. The beginner who is reading this should know that:
- each command may have a "local" syntax of how to handle arguments. The expr command is one such command.
- each argument may also be interpreted simply as tcl command/script, expression, or name of a variable.
- if expects an expression as its first argument, therefore there is no need to code the expr explicitly.
- for expects a command/script as its first argument, therefore no [] necessary.
- time expects a command as its first argument. time [command...] would substitute the script between [] upon Tcl parsing, and then the command time uses its result and treats this as a script. this is an example of double substitution.
- switch is (at a first glance) confusing to C-programmers because they do not see the C case. The well defined order and interpretation of the switch arguments as 'pattern-body' pairs enables proper operation of switch.
- if (second look) is at a first glance possibly confusing to experienced tcl programmers when they would encounter the command the first time. Corresponding to switch one should expect a chain of arguments: basically expr, if clause, else clause. A little bit loss of consequent Tcl habits arises when the elseif clause comes into focus. A "keyword" is required here in order to let if recognize a repeat of expr etc. arguments. Besides this "keyword", if allows the usage of then and else as dummy words to serve code readability. See the documentation for if.
- incr expects a variable name. Perhaps a bit silly to notice that no $ is "necessary". But it may help a beginner to understand why sometimes not to use $ and when not. BTW: $ would effectively result in double dereferencing. See also dereferencing. There are a couple of l* commands which are the same - and equally confusing.
- puts expects nothing. Uh, this means that the argument is taken as processed by the parser. And send to the console in this form. That's why variable substitution in a string with white spaces must be enforced with enclosing "..".
A bit confusing for beginners is the use of list in -command options of Tk widgets or as the script argument of bind. The usage of [list .....], results in immediate command substitution. Its result is treated as the effective command/script for the -command option or binding. It's all in the "Division of Responsibility" between the parser and the command: Any substitution is carried out during parsing.
RJM: I have queried my sources for the character sequences if [expr and if {[expr. Indeed I used it when more complex expressions were defined. During this query, I surprisingly found these character sequences in several other sources, too. This includes the activestate distribution (mainly the second character sequence). I'd encourage the reader to check his/her own sources, too.
RS: Note that the example above,
if {$a < 0} {set a 0}could also be written in the little language of expr:
set a [expr {$a<0? 0: $a}]Matter of taste, of course.RJM: Another example pair also shows how argument expectations influence quoting/substitution:
set path [expr {$b eq "" ? [pwd]: [file join $b]}]
set path [if {$b eq ""} pwd else {file join $b}]The first example needs explicit command substitution, while the second one already expects commands. Note that if returns the result of the executed command (remark: I also added a second if entry in the bulleted list of commands above).