Many long time Tcl users are positively passionate about the language because of some of the wonderful things you can do with Tcl and Tk. It isn't so much the portability, or the extensions, or the internationalization (unicode) support, or even the fact that Tcl programmers are better looking than most users of those other languages. The simplicity of the Tcl language itself, coupled with the highly dynamic and introspective nature of the interpreter make wonderful things possible. Unfortunately, all these little gems are not obvious to the first-time Tcl user. In fact, it sometimes takes quite a while to realize just how powerful Tcl can be in the "right hands." There is nothing at all wrong with using Tcl and Tk as you would any other programming language, but if you learn and use a few little tricks like these, you will be a more productive person, and you might even have a little more fun while you're at it. This page lists a few of those "Tcl Gems" that have been developed over years and years and years and years of experienced Tcl use. Some of it is just pointers to other pages, and some is just a description of what you can do. Call this advocacy if you like, or just a collection of interesting reading, but I hope you enjoy these. You might also want to check out [Tk Gems] if you're interested in some clever little tricks with different kinds of widgets. ''RWT'' Besides gems, sometimes a good pebble would be o.k. Have you checked the [Bag of algorithms], [Tcl examples], and [Bag of Tk algorithms], just in case...? ''RS'' ---- '''auto update''' [Richard Suchenwirth] shows how to set up a source file that automatically reloads itself into the interpreter every time you save it. Great development aid! Look for ''autoupdate'' on [Braintwisters]. There are some other cute ideas on that page, including ''dynamic variables'' and ''intgen'' which returns a new unique integer each time called. ---- '''re-source''' [Jeffrey Hobbs] posted on comp.lang.tcl that TkCon has a command ''tkcon resource'' that he uses all the time in development to reread the script in (it is extremely handy to not have to exit and restart wish). It not only resources in the main interpreter, but reinitializes the primary slave environments. TkCon is a gem in itself. If you don't have it, get it. ---- '''Reload a package''' [HD]: Here's something I use all the time: proc reloadPkg {pkg} { eval [package ifneeded $pkg [package require $pkg]] } Example: package require XYZ # Discover bug in package # Fix it in editor reloadPkg XYZ ---- '''editing procedures ''' [Richard Suchenwirth] says: In my Tcl sandbox, I often don't even care to source a file at runtime, I typically rather * display the full proc (name, args, defaults, body) into a text widget * edit it (maybe just insert a [[puts]]) * eval the text widget's content, so the proc is redefined. This way debugging doesn't leave traces in the original files, it's even faster than sourcing, and very intuitive. Oh, if it was a successful change, I'd also make it persistent in the source file - just ask auto_index where the proc came from ;-) and [Harald Kirsch] says that one of his favorite procs is proc learnSelection {} { set s [selection get] uplevel \#0 eval [list $s] } ''DKF: Whyever not the following instead?'' proc learnSelection {} { uplevel \#0 [selection get] } ''Sometimes you just have to wonder about people...'' proc learnSelection {} {uplevel #0 [selection get]} is also equivalent - hashes matter only at beginning of command - ''RS'' ''unless you're using a syntax-sensitive editor like emacs or Source Navigator, which is not quite clever enough ...'' ---- '''new language constructs''' Tcl is almost unique in that you can create new language constructs if you really need them. * For an example of adding a new loop control feature, take a look at [do...until in Tcl], which also doubles as a [do..while] construct, if you happen to prefer that logic. * A Java-style [try ... finally ...] has also been implemented. * [returneval] lets you return from a procedure and evaluate a script "in its place". * Very old (or very different) language constructs have also been considered, see [Goto in Tcl], [Basic in Tcl], [RPN in Tcl], [Playing APL]. ---- '''Unknown possibilities''': One of the few Tcl rules says that the first word of a command is the command name, and you can even legally break that, allowing for constructs like i = $j+1 ==== set i [expr $j+1] a ( 2 ) ==== lindex $a 2 {1 .. 5} ==== {1 2 3 4 5} without having procs ''i'', ''a'', ''1'' - that would be the known way. See [Radical language modification], where this is done with the ''unknown'' command. [Gadgets] o.t.o.h. do "infix assignment" more orthodoxly, with "light-weight objects". [Let unknown know] if you want to build language modifications incrementally... ---- '''Tcl as OS glue''': In the amazing ETLinux project [http://www.etlinux.org], an embedded system is developed that packs stripped-down Linux and Tcl (7.6, enhanced by mount, ifconfig, route, uuen/decode, dup, fork, kill, nice, ...) into a tiny 386SX/2 MB RAM/2 MB disk configuration. Tcl scripts do the ''init'' (first process at all), mail server, web server, ... ---- '''No-Op Prompts''' ''DKF:'' When I'm running ''tclsh'' or ''wish'' interactively, I have my prompt set to the usual default of ''%'' and have the following little procedure in my ''.tclshrc'' (see "[The RC File]" for more on tclshrc) proc % args {uplevel 1 $args} This lets me copy and paste whole lines of Tcl with the mouse triple-clicks without having to worry about stripping leading prompt symbols, so making it faster for me to edit sessions interactively (particularly when you want to change part way through an existing procedure definition.) Power-user type facility? You betcha! Useful? Certainly so for me. I just wish I could do this with other interactive scripting systems... ''mikeH:'' Note that you can also set your prompt to the semi-colon and then a space ''; '' to achieve the same goal without doing extra evals etc.. this trick also works nicely for traditional sh style shells like bash ---- '''Swapping variables:''' foreach {a b} [list $b $a] break ;# RS ---- '''Constants:''' [Constants] that shall be visible anywhere in the code must be global. Global variables need however be declared every time you use them, either with ''global Pi'' once per proc or ''::Pi'' once per use. Or, you can proc'ify them, since procs are global too: proc constant {name value} {proc $name {} [list return $value]} constant Pi 3.1415926535 puts "Pi = [Pi]" ---- '''Whole or in pieces:''' A proc may be called with either a variable number of non-list arguments, which is convenient interactively, or a list of such arguments (which is good if you have the list anyway - no need for ''eval''), if you interpret the ''args'' parameter as follows: proc lsum {args} { if {[llength $args]==1} {set args [lindex $args 0]} expr [join $args +]+0 } ;# RS lsum 1 2 3 lsum $a_list_I_got_from_somewhere ---- '''[An analog clock in Tk]''' is a beautifully simple and simply beautiful piece of canvas demo code. ---- '''Optional arguments''' HD: [Laurent Demailly] has written the package '''opt''', which looks very powerful and comprehensive, but I find the following approach very easy to implement and adequate for my needs. Let's say I start with a proc that takes a long list of arguments, each having a default value: proc myProc {{arg1 dflt1} {arg2 dflt2} ... {arg9 dflt9}} { This requires the user to know the order of arguments, and it's a pain if you only want to specify a non-default argument for arg9. It would be nicer if we call myProc in this case as: myProc -arg9 Fred So let's rewrite myProc as: proc myProc {args} { # Set up default values array set the_args { -arg1 dflt1 -arg2 dftl2 (etc ...) -arg9 dflt9 } # Now overwrite with user-supplied values array set the_args $args # And off we go... puts "The ninth argument is $the_args(-arg9)" } In practice I usually have procs like this in a package: package provide XYZ 1.1 namespace eval XYZ { variable Global array set Global {etc ...} } proc XYZ::myProc {args} { variable Global array set the_args [array get Global -*] array set the_args $args # Carry on as before } ---- '''Use profile with ?# command''' [Chang LI] changl@neatware.com I defined the ?# command as proc ?# {args} { if {[info exists ::tcl_platform(debug)]} { uplevel 1 eval $args } } '''US''' Very useful, but one round of evaluation too much. Should be: proc ?# {args} { if {[info exists :tcl_platform(debug)]} { uplevel 1 $args } } ? means the alternative comment. It was quite useful to add profile code and you do not need to delete them when you distribute the code later, just set the tcl_platform(debug) as 0. set tcl_platform(debug) 1 ?# package require Tclx ?# profile -commands on # your tcl code here ?# profile off report ?# parray report ---- '''LISPlike lists:''' [Kevin Kenny] emulates mutable lists built of cons cells with procs in [Tcl and Lisp]. ---- '''List Comparision:''' Ed Suominen (a TCL beginner as of Sept. 2001) contributes the following simple but fast proc for comparing lists. The result are two lists containing items exclusively in one argument list or the other. # PROCEDURE: LISTCOMP # CALLS: none # RETURNS: lists of unique elements in each list # USAGE: list1, list2 are the lists to compare. # outname1, outname2 are names of lists to use as the # output of the procedure # Copyright (c) 2001 Edwin A. Suominen, http://eepatents.com # This procedure code is freeware under the terms of the no-endorsement # version of the BSD License, incorporated herein by reference, with # = Edwin A. Suominen, = author, = 2001. # Inventors of any inventions that employ or are based on this # procedure retain their patent rights to such inventions. proc listcomp { list1 list2 out1Name out2Name } { ### Define empty lists in case one has no unique elements set out1 {}; set out2 {} ### Test each element of each list against all elements of other list foreach {i} $list1 {j} $list2 { # First, test for unique element in list1 if { [ lsearch -exact $list2 $i ] < 0 } { lappend out1 $i } # Then test for unique element in list2 if { [ lsearch -exact $list1 $j ] < 0 } { lappend out2 $j } } ### Put results in specified lists upvar #0 $out1Name x set x $out1 upvar #0 $out2Name x set x $out2 ### END LISTCOMP return } [Setok] Wouldn't it be better to sort the list first (Ologn) or build an array with the elements as keys? As far as I can tell the above can lead to On^2 time. ---- '''lshift for command-line argument and procedure args parsing''' #========================================================== # NAME : lshift # PURPOSE : shift list and return first element # AUTHOR : Richard Booth # http://www.lehigh.edu/~rvb2 # rvb2@lehigh.edu rvbooth@agere.com # --------------------------------------------------------- # ARGUMENTS : # % inputlist # List to be shifted. # RESULTS : # * Sets inputlist to 2nd to last elements of original inputlist # * Returns first element in inputlist # NOTES : # * useful for command-line arguments and procedure args processing # EXAMPLE-CALL : # # while {[llength $argv] > 0} { # set arg [lshift argv] # switch -- $arg { # -lib {set lib [lshift argv]} # -show {set show 1} # default {lappend tests $arg} # } # } # #========================================================== proc lshift {inputlist} { upvar $inputlist argv set arg [lindex $argv 0] set argv [lrange $argv 1 end] return $arg } See also [Stacks and queues]. ''JCW - Here's a non-recursive directory walker based on it:'' proc walkdirs {args} { set files {} while {[set dir [lshift args]] != ""} { foreach x [glob -nocomplain [file join $dir *]] { lappend [lindex {files args} [file isdir $x]] $x } } return $files }