gotcha

Difference between version 36 and 37 - Previous - Next
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 variable
rather 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 to
convert the input into a proper list. Examples of sanitizing 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 not
trigger 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}
======

*** Magic characters in filenames and arguments ***

The [open] command has special handling when its filename argument begins with |.  The [exec] command has special handling when any argument begins with | or < or > or 2> or when the final argument is &.  Normally this is fine, but if one of these arguments is not under the programmer's control, unexpected behavior can result.

One may work around [open] by adjusting the filename if it begins with |.

======
set tmpf $filename
if {[string match |* $tmpf]} {set tmpf [file join . $tmpf]}
set channel [open $tmpf]
======

There is a proposal ([TIP 424]) for changes to [exec], but as of this writing (May 2020), it has not been implemented in the core language.

*** 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`

*** [chan copy] bytes or characters ***
If the encoding of the two channels matches, the given size is in bytes,
otherwise it is in characters.



** Misc **

[Larry Smith]: I was expecting a new control construct of some sort.

======none
gotcha {
  ...
} now {...}
======

** Page Authors **

   [PYK]:   Added the [chan copy] section.


<<categories>> Glossary