Version 25 of Dangers of creative writing

Updated 2007-01-17 16:22:29 by LV

Richard Suchenwirth 2000-11-21 - "Creative writing" in this context means assigning a value (writing) to a variable referred to by name, that is created if it doesn't exist. The following Tcl commands are "creative writers":

append - array set - binary scan - catch - dict append/create/others probably - file stat/file lstat - foreach - gets - incr - info default - lappend - lassign - lset - proc - regexp - regsub - scan - set

When code is evaluated in a namespace, but outside of a proc, the namespace rule for name resolution applies: If you provide a fully-qualified name that starts with a ::, there is no question about what command, variable, or namespace you mean. However, if the name does not start with a :: (i.e., is relative), Tcl follows a fixed rule for looking it up: Command and variable names are always resolved by looking first in the current namespace, and then in the global namespace. (TclHelp, namespace man page)

This leads to dangerous context-dependency: depending on what global variables exist, new variables are created either here or there. Ronnie Brunner wrote in the comp.lang.tcl newsgroup:

 % set a "global"
 global
 % namespace eval foo {set a "namespace"; set b "namespace"}
 namespace
 % set a
 namespace
 % set foo::a
 can't read "foo::a": no such variable
 % set b
 can't read "b": no such variable
 % set foo::b
 namespace

Hemang Lavana commented: To be safe, you should always use the `variable' command first to declare a variable in the current namespace.

RS: But still, this requirement for a declaration is not in the spirit of Tcl.

Proc definitions inside a namespace remain in that namespace, however; they don't go out to the global namespace, as Ronnie further showed:

 % proc a {} {puts global}
 % namespace eval foo {proc a {} {puts namespace};proc b {} {puts namespace}}
 % a
 global
 % foo::a
 namespace
 % b
 ambiguous command name "b": binary break
 % foo::b
 namespace

RS: I think namespace resolution should distinguish between locating existing things (procs or variables) and determining an absolute name for non-existing things.

  • For locating existing things, the current resolution rule can be left unchanged.
  • For "creative writing", creating new things, it should confine itself to the current namespace (as it currently does with procs). He who wants something done in the global namespace, can explicitly prepend :: to the name ;-)

NEM 9Sep05: Just looking at this old discussion again, a pertinent question would be what should be the appropriate behaviour in this situation:

 set a 12
 namespace eval foo { set a [expr {$a+1}] }

Here we apparently have the same variable referenced twice, but what should be the effect of the second statement? There are a number of possibilities:

  1. ::a (global) gets set to 13, ::foo::a doesn't exist (current behaviour of set);
  2. ::a is left as 12, ::foo::a gets set to 13 (call this a variable copy-on-write behaviour. Behaviour of the variable command);
  3. error "no such variable 'a' while executing 'expr {$a + 1}'" (lookup in current namespace only);

There are probably a few other options, too. Of these, both 2 and 3 seem much better than option 1. I used to think option 2 would be the best (which looks like RS's suggestion, too), but option 3 might be the clearest one of all, and is the behaviour that we get in proc bodies now (all vars are local for read or write unless explicitly qualified or imported).

EKB I'd vote for #3! In the case where the definition of a outside my namespace is far removed from where I'm using it, behaviors #1 & 2 could be very confusing.


Update on 2007-01-16: Ralf Fassel pointed out another "creative writer" in comp.lang.tcl:

 checkbutton .foo.bar.baz 

will create/use a global variable 'baz' which might have surprising effects if 'baz' is already in use as global variable with a different meaning elsewhere in the program.

LV I wonder if you investigate Tk, whether you will find more of these.

RFox I think radiobutton does similar stuff.

DKF: The rule is "always set the -variable for a checkbutton or radiobutton".

AK: So why not make this option required ? (Is that actually possible in Tk ?)

DKF: You've just answered why. There is no mechanism in the current option management code to make any option required during object creation (which is the only time you'd ever want it to be mandatory anyway).


TIP#278 [L1 ] would take away the namespace problems with respect to creative writing, but apparently not the checkbutton problem.


[ Arts and crafts of Tcl-Tk programming | Category Internals ]