Forum OpenACS Q&A: "good" tcl code

Collapse
Posted by Jonathan Ellis on
What are the worst tcl habits of acs 3.x code? And what are the important extra features available in tcl 8? Since most (all?) of ACS 3.x is tcl-7 backwards compatible, and that's where I picked up tcl, I'm trying to figure out what tools I haven't been using just because I wasn't aware they're there...
  • tcl 8: non-greedy regexp options
  • tcl 8: package isn't new, but it's unused in acs 3... namespaces are new
  • what idiot added empty_string_p, which has the overhead of a proc call, when {$string == ""} works fine (and doesn't try to pretend tcl is lisp, which could be off-putting to some)
  • others?
Collapse
Posted by Michael A. Cleverly on
What idiot added empty_string_p? That's from Philip's original utilities.tcl file... :^)

And, actually, you don't want to do {$string == ""} because that can blow up if you have something that looks like it could be an integer (12345678901234567890) that can't be represented as a 32 bit integer, OR, something that looks like an Octal number, but isn't: 08 (for August), for example. Much better (in Tcl 8.3) to use [string equal]. Tcl 8.4a3 has {$string eq ""} ...

Namespaces, advanced regular expressions, ability to handle binary data, and Unicode support are the biggies. Other new things:

  • string map can replace (multiple) regsub's more efficiently in many cases
  • foreach can take a list of variables (instead of a single variable). Thus, instead of:
    set foo [lindex $args 0]
    set bar [lindex $args 1]
    set baz [lindex $args 2]
    you can employ:
    foreach {foo bar baz} $args break
  • You can find a pretty detailed list of changes between Tcl versions at http://mini.net/tcl/2.html?changes.

Collapse
Posted by C. R. Oldham on
AOLserver 3.3.1+ad13 has Tcl 8.3.2, right?

And AOLserver 3.4.2 that someone is working on has 8.3.4?  And Tcl 8.4 is not released yet?

Collapse
Posted by Jonathan Ellis on
yeah I was aware how tcl tries to cast strs to numbers when it can, but I didn't realize that meant it would do

tcl>puts [expr "123453454354" == ""]
Error: integer value too large to represent

that's pretty cool about the foreach, didn't know that...

Collapse
Posted by Don Baccus on
Also handy in a mixed SQL/Tcl environment where "t"/"f" is used rather than 0/1 for booleans (PG's built-in bool type, in Oracle by convention in OpenACS) is "string is false" and "string is true".  If you use it you can ignore how the boolean's being generated.
Collapse
Posted by Tom Jackson on

empty_string_p isn't that bad. It seems like anytime you can condense even a few statements into one simple proc, it is worth it in readability of your code. But occasionally I wonder if empty_string_p is available, and I use instead:

string match "" $string

Which seems pretty easy to understand all by itself.

Collapse
Posted by Andrew Piskorski on
The empty_string_p proc was certainly a pretty good idea at the time it was created - remember, that was a long time ago, back in some really old Tcl 7.x version. If I remember correctly, the implementation of empty_string_p was changed several times over the years to take advantage of changes and improvements in the Tcl core. And the oft seen {$foo = ""} alternative, was never the right code to use, as others have pointed out above.

The proc should, however, have been named empty_p - the 7 extra characters of "_string" are unnecessary and undesirable in such a basic function that is used so frequently.

Now that the "string equal" command exists, there would probably be no good reason to create an empty_string_p proc if starting over from scratch today. (The "string match" command is doing something more general and complicated and is thus a poorer choice.)

As for performance, note that in the unlikely even that it turned out to be a performance bottleneck, AFAIK, it would be entirely plausible to add an empty_string_p bytecode command to the Tcl interpretor, which would make empty_string_p absolutely as fast as possible. I'm not sure though whether you could do that just by loading in some C code with "package require", or if it would mean hacking the Tcl core. (And unfortunately, standard Tcl does not have a macro facility to let you do that sort of performance tweaking at a higher level without hacking C code.)

Collapse
Posted by Don Baccus on
The modern thing to do, as mentioned above, is '$string eq ""' which is a built-in operator that doesn't suffer from the problems '$string == ""' suffers from.
Collapse
Posted by Andrei Popov on
> doesn't try to pretend tcl is lisp, which could
> be off-putting to some

If I am not mistaken, it is even documented in best practices that you *should* do a _p for booleans. Pretence or not, it is a good practice that makes things a lot clearer.

Collapse
Posted by Mark Aufflick on
Is it possible to compile aolserver 3.3 with tcl 8.4? Or should I just take the hit and switch to aolserver 4.0 for tcl 8.4 goodness?
Collapse
Posted by Jonathan Ellis on
No, that's not possible in 3.3, but 3.5 has the "build your own tcl" approach of 4.0.
Collapse
Posted by C. R. Oldham on
And we are running 3.5.6 in production with Tcl 8.4.3.  The only gotcha is that there is no support for "ns_charsets", and sometimes returning high-bit characters causes the dreaded  prefixes.