A '''gotcha''' is an ''unexpected side effect, behavior, consequence, requirement, etc.''
** See Also **
[Dangerous constructs]:
** Description **
As with any language, the syntax and semantics of [Tcl] can catch a programmer
off-guard. This page is describes patterns and behaviours that can be
considered as '''gotcha'''s, and is somewhat arranged by prominence and
severity.
** Gotchas **
*** [Tcl and octal numbers%|%The Octal Bug%|%] ***
See [Tcl and octal numbers]
*** `[set]` and namespaces ***
[Twylite] 2012-12-12 [PYK] 2019-12-09: `[set]` might set a global variablerather than a variable in the [namespace current%|%current namespace]. See
[Dangers of Creative Writing].
======
set foo 10
namespace eval bar { set foo 20 ; set bar 30 }
puts $foo ;# puts: 20
puts $bar ;# error: can't read "bar": no such variable
======
*** [Brace your expr-essions%|%Unbraced Expressions] ***
The following script will never end:
======
while [llength $args] {
lassign args arg
}
======
It should instead be:
======
while {[llength $args]} {
lassign args arg
}
======
See [while] and also [Brace your expr-essions%|%Brace Your Expressions].
*** Assuming that a value is a [dict%|%dictionary] ***
In the following example the fact that `$dict` is not a dictionary does not
trigger an error:
======
set dict hello
dict exists $dict Bob ;# -> 0
======
*** Assuming that a value is a [list] ***
Another Tcl '''gotcha''' is to hand arbitrary strings, read from the user or a
file/command, directly into a list operation without first ensuring that the
contents is, in actuality, a [list].
[DKF]: The best way to deal with such user input, where users aren't expecting
to write a Tcl list in the first place, is to use a sanitizing command toconvert the input into a proper list. Examples of sanitizationg commands can
include [split], [splitx] and (my favorite) this:
======
set elements [regexp -all -inline {\S+} $line]
======
*** Assuming that a variable is an [array] ***
Using `[array unset]` on a variable that isn't array does nothing, and does nottriggeer an error:
======
set array 5
array unset array
set array ;# -> 5
======
*** [lsearch] default matching style is `-glob` ***
Forgetting to use `-exact` when that's what is intended can lead to surprises.
*** `switch` and its Arguments ***
[RS] One possible gotcha is `[switch]` -- always use `--` before the switch
variable, since if the value of the switch variable starts with `-`, you'll get
a syntax error.
[AMG]: This isn't required by Tcl 8.5 onward.
*** Delimiting Options from Arguments ***
[KPV] Also, [comment]s within [switch], while possible, are tricky.
[RS] 2010-05-10: A similar gotcha is in the '''[text] search''' subcommand -
although the misunderstanding could be avoided by counting non-optional
arguments from the end,
======
set whatever -this
$t search $whatever 1.0
======
raises an error that `-this` is an undefined switch. For robustness, use
======
$t search -- $whatever 1.0
======
if the slightest possibility exists that $whatever might start with a dash.
[PYK] 2017-05-19: [scripted lists] makes comments within a switch possible, but
it needs to move into the Tcl implementation level to be performant.
*** Interpretations by `[expr]` ***
[RS] 2010-02-24: Yet another gotcha we ran into last night: Consider a function
======
proc f x {
if {$x == 00000000} {
puts "$x is NULL"
}
}
======
which reported:
======none
0E123456 is NULL
======
How so? Bug? No -- feature. With the `[==]` comparison operator, the operands are
tried to match as integers, floats, or then as strings. And `$x` in this
case, though meant as a pointer in hex, could be parsed as float - with the
numeric value of 0, which is numerically equivalent to 00000000. The solution
of course was to use the `[eq]` operator instead.
[AMG]: Another issue with `[expr]` interpreting stuff happens when said stuff was
already interpreted by Tcl. This creates performance, security, and
correctness problems. Always [brace your expr-essions]!
Another example:
======
expr {{} < 0} ;# -> 1
expr {{} > 0} ;# -> 0
expr {{} == 0} ;# -> 0
======
For the `<` and `>` operators, `{}` is interpreted as an integer, `0`. For the
`==` operator, 0 is interpreted as a string.
*** `[string is]` and the empty string ***
`[string is]` always returns 1 for the [empty string]. To avoid this, use
`-strict`
*** Confirming that a value is an integer decimal notation ***
The `[string is]` commands accept all the numeric notations that `[expr]`
does (see [Tcl and octal numbers%|%The Octabug], so they aren't up to the job
by themselves. Here is one way to do it:
======
set mynumber 0x11
expr {[string is entier -strict $mynumber] && [scan $mynumber %d mynumber] > 0}
======
*** Partial sub-command resolution in [namespace] [namespace ensemble%|%ensembles] ***
======none
namespace eval table {
namespace export *
namespace ensemble create
}
proc {table::spoon fork} x {
return $x
}
======
Even though there is no `spoon`, there is:
======none
% puts [table spoon huh?]
huh?
======
To change this behavior:
======
namespace ensemble configure table -prefixes no
======
*** Nested `[vwait]` ***
See `[http://wiki.tcl.tk/1302#pagetoc914efb06%|%Avoiding Conflicting vwait's] on the [vwait] page`
*** Starving Queues with `[after]` ***
See `[http://wiki.tcl.tk/808#pagetoc0f64563c%|%after x after idle] on the [after] page`
** Misc **
[Larry Smith]: I was expecting a new control construct of some sort.
======none
gotcha {
...
} now {...}
======
<<categories>> Glossary