Tcl -- Tool Command Language
Some important websites for more information and downloads:
Description of the Tcl language
Easily extendable script language which became most famous for the "Tcl Tk Gui Toolkit" and "Tcl Expect", as well as for easy embeddability, but it is increasingly used in other spheres. Some of its most unique features:
Everything is a string (native representation is used behind the scenes). All values can be used inter-changeably and anywhere where a string can be used for natural serialization.
Extremely minimalistic syntax: cmd arg arg arg ...
No reserved keywords
No static language constructs (for, while etc. are just commands like any other)
Flexible event-based programming. F.ex. IO and GUi events call callbacks which greatly reduces the need for threads
Has a threading system, too.
Everything can be dynamically changed and replaced (remember: language constructs are also just commands too!)
The Virtual Filesystem mechanism
Doesn't force any particular programming paradigm. For instance, a wide range of OO extensions exist from the more traditional [incr Tcl] to the more dynamic Extended Object Tcl and delegation -style Snit.
Cons of the TCL Language:
Not terribly fast, perl and python are generally faster. Usually fast enough though.
As usual, it depends on exactly what you are doing, with common benchmarks often being written to make other languages look good. The old quote about "lies, damned lies and benchmarks" does apply.
Events in Tk are evaluated at the top level scope. No closure tricks for you.
Lots of "gotchas" with respect to quoting
This is a bit inbetween really; the quoting rules are actually very simple, but beginners do sometimes have problems, as they infer extra semantics which aren't really there (typically by assuming {} marks blocks of code like in C).
I find it's difficult to x-ray or study all the potential levels of quoting and escape indirection unwinding. It's sort of the quote-level version of Thick Bread Smell. I'd rather break things up a bit, Divide And Conquer and have a language that facilitates such.
No anonymous functions - packages that do appear to provide them create global objects that are not garbage collected among other problems.
However anonymous code fragments are available, and version 8.5 allows these fragments to have scopes.
Doesn't the "apply" command in Tcl 8.5 provide anonymous functions?
Suffered under a disgustingly hype-laden regime under scriptics. ("Leverage your cross-functional b2b synergies with scripting!") Thankfully no longer a problem after the dot com crash.
More on TCL
It is untyped and uncompiled [-- It does, however, utilize a bytecode compiler for efficiency now, making it about as slow as other scripting languages]. Tcl is much more a dynamic language than comparable scripting languages, and makes use of data as code model to great advantage.
TCL's main attraction is its small footprint, ease of network and GUI programming, event model, write-once-run-many (programmers usually have to go out of their way to make Tcl programs platform specific.)
It has a C API which makes embedding an interpreter easy (making it an Embedded Language). Passing data back and forth is easy, as is writing extension commands to the interpreter.
See wiki.tcl.tk (a two-player car racing game in 127 LOC) or wiki.tcl.tk (pages on The Tclers Wiki) for examples of why some people like Tcl so much - brevity and automation of complex tasks.
Tcl was invented by John Ousterhout while he was a professor at Cal Berkeley.
Tcl has several implementations and is widely available on many platforms: Unix, Mac Os9/ Mac Osx, Windows, Palm OS, Windows CE, MSDOS, several realtime OS's, and as a browser plugin for Mozilla, Netscape, Windows IE. Tcl is also available as Jacl, a Tcl port to Java, thus it also runs anywhere Java runs. See: tcljava.sf.net
Hiring Tclers
You will find a list of Tclers interested in doing contract work or working full time here: wiki.tcl.tk
Comments
Indeed, Tk is popular; it can even be accessed from Python and Perl (and Erlang). -- Falk Bruegmann
Tk is a common reason Tcl is widely used. It is a very good GUI toolkit: lightweight, easy to use, simple to extend. It had a Motif look and feel,but was far simpler to use than Motif. It now uses the local look-and-feel of the platform on which it is running [- which is Motif on UNIX]. It is quite common that programs written on Unix/X11 runs unchanged on Windows and Mac (the same is true for Windows to Unix.)
Tcl as a language is highly dynamic and provides useful introspection facilities. If one writes idiomatic Tcl, using Meta Programming techniques, rather than using it in the way one writes C or Pascal, one can write efficient and elegant programs.
-- Nat Pryce
Nat, how are those techniques when used for Tcl? I've written quite a lot of Tcl/Tk code but I am not sure of what you are talking about. A friend and I were thinking about writing a Tcl module so we could make an abstraction layer for access to databases. The idea was creating something similar to DBI in Perl or JDBC in Java, but for Tcl (starting with postgresql). I got a nice design and when I sent it to my friend, he had already written a prototype, which created a 'database widget', similar to a Tk widget, indeed virtually indistinguishable. I found the idea fascinating and so we took that approach. Is this that metaprogramming you're talking about? -- David De Lis
Metaprogramming is writing code that, itself, writes code. The code that is generated represents application data. For example, a Tcl script could parse an XML document to create another Tcl script that has same structure as the document, and in which commands are named after the nodes of the document. The top level script can provide implementations of the commands in the "parse tree" to process the nodes. Evaluating the parse as a script then processes the parsed data.
So processing and parsing are performed by:
proc node_NODE1 { ... } { ... }
proc node_NODE2 { ... } { ... }
...
eval [xml::parse $file]
The basic advantage of this is that, instead of interpreted code manipulating script-level data structures, you have C code (the interpreter) manipulating the data structures that represent the language.
Functional composition
Here is a small but mighty example of metaprogramming: functional composition
proc o args { proc [set name [info level 0]] x " [join $args { [}] \$x [string repeat \] [expr {[llength $args]-1}]]" set name }
Maybe some explanations are necessary. A procedure named "o" is created (popular operator for function composition) which takes any number of arguments (indicated by the "args" special name). What the proc does is create another proc, with a name exactly as its creator was called (this is what [info level 0] returns), and exactly one argument, called x.
The body of this generated proc joins the args arguments (function names) with a space and an open bracket, indicating embedded evaluation, then the argument x - dereferenced by $ but deferred by the prepended backslash - and finally a sequence of closing brackets, one less than the length of the originally input args.
For example, consider the composition of three functions named "foo", "bar", and "grill":
% o foo bar grill o foo bar grill
The returned result is the name of the generated procedure (not a true lambda, as it has a name, but coming close). It contains spaces, but that is no problem in Tcl, just quote accordingly. For instance, if we want to see the body of the generated procedure,
% info body "o foo bar grill" foo [bar [grill $x ]]
See? A "nest of invocations" of the three functions has been created, and calling "o foo bar grill" with an argument just combines the three functions as intended. Richard Suchenwirth
More comments
More than a few programmers have been led into the world of Tcl because Philip Greenspun chose to base his open-source ACS on AOLServer. Some very high volume web sites run AOLServer.
Tcl is a powerful language when we use it for CAD applications.
Tcl had, as its original purpose, to provide a common framework for writing the tiny languages that many applications have. In particular, think of things like spreadsheets, drawing programs, statistics programs - anything which requires one to issue commands or to take several actions - say a mail program for instance. Tcl is great for creating tiny languages with a lot of power. And its syntax is simple enough to teach to nonprogrammers. If they can get their brain around which brackets go where to express a simple if-then-else. Who would teach non-programmers to program in Tcl when there are Python and Ruby???
Someone who doesn't know (or know of) Python or Ruby, someone whose student will be encountering Tcl (think of teaching a physicist how to moderately change a few widgets on an preexisting lab setup,), various other reasons. I like Python and Ruby too, much more so than Tcl, but that's no reason to start yet another Language Pissing Match.
Also, Tcl's syntax is considerably closer to natural languages: the words of a command are just separated by whitespace (like in LISP and Forth). So it's syntactically easy to write a Tcl procedure that parses and executes (that's the harder part)
read my mind and do what I mean
which in many other languages would have to be
read("my", "mind", "and", "do", "what", "I", "mean");
And upvar is confusing enough to tie them up in knots for ever more. Tcl is a tragic case of a language design being severely degraded by a single feature.
FUD. Upvar is 1) not needed all that often, 2) a very simple concept: given the name of a variable, and optionally a context (global or stack level), upvar sets up an alias so you can access that variable (r/w) via a local name. It's indispensable for Tcl's by-value / reference-free approach to data structures. -- jcw
Yes. One should only use upvar when he wants to return two or more variables from a procedure and doesn't want to do that in a list.
To explain it even simpler: the [upvar] command is Tcl's way of Call By Name, pretty equivalent to passing a pointer to a variable in C (only that you can specify the scope in which the name applies). It is mostly used to pass Tcl arrays (hash tables in fact), which can't be done by value. But it, together with [uplevel], also allows building all kind of debugging tools very simply. Here's an example - append an item to a list if it isn't there yet:
If that explanation were in the manual entry for upvar or in the tcl tutorial at www.tcl.tk , far less people would be confused by upvar.
proc ladd {listVar item} { upvar 1 $listVar list if {[lsearch $list $item] == -1} {lappend list $item} } set aList {2 4 8 16} ladd $aList 16 ;# doing it wrong; you should pass the name of # the variable, not its value. ladd aList 32 ;# aList is now {2 4 8 16 32}. ladd aList 4 ;# aList is unchanged.
Another example, first in C:
void f(int x, int *y) { *y = x; }
then the equivalent in Tcl:
proc f {x _y} { upvar $_y y set y $x }
The difference to LISP (with its special forms and macros) is that you can always know whether you pass a value (as constant, or with "$" notation), or a variable name (via upvar) that may have side-effects, like changing the list, in the example.
In general, I suppose people experienced in languages of the Fortran/Algol/C/Java heritage may sometimes be bewildered by Tcl's concepts, but coming from LISP, one might very soon feel at home...
Tcl War with Richard Stallman
Several years ago, there was a big bruhaha between Richard Stallman and TCL fans. Richard claimed that TCL does not scale to large applications, and thus should be avoided in case small applications grow large. (Naturally, RMS perferred Lisp Language.) Personally, I suspect this depends on one's development style. If one uses a task-oriented and event-driven model, then "large application" is generally meaningless because you simply have a bunch of small programs that respond based on the state as recorded in files or databases with a few parameters passed between each script.
Unfortunately, Richard Stallman was right but did not put his point across in a politically astute way. His argument was that Tcl is a rather poor foundation for a universal language with which to make applications scriptable, and that it would be better to embed a Scheme interpreter into applications and compile different scripting languages to Scheme at read-time. Now we have Parrot and .NET both trying to be virtual machines for different languages, both of which can be used as embedded interpreters. However, Scheme would be a more elegant representation than bytecodes.
More comments
I chose Tcl as my first (and, so far, only) programming language because it's simple and comprehensible; it's oriented more toward text and graphics (with the Tk toolkit) than toward maths and logic; it doesn't require the programmer to spend any time on loathsome tasks such as compiling; and yet you can do a whole lot of things with it.
-- David Mc Clamrock?
Tcl is by far the simplest of the major scripting languages to extend with C modules. Especially using critcl. However, they all have their place. Even vbscript :) -- Pat Thoyts
Every time I have to do another project in Tcl/tk, I'm amazed at how easy it is to get my ideas into code quickly, and more importantly, correctly!
I don't like C++, as everything is sooo complicated. I do like Tcl/Tk, because it's not. -- Philipp
I am not sure what is happening in constructs like this:
$t insert end "Hello World!"
The "t" variable is referencing some kind of widget (in this case a text GUI widget). What is happening in this case from a linguistical standpoint? How does it know which widget to operate on, especially if there are multiple text widgets?
the $t will be expanded to the value stored in t, which as you say is an instance of a widget. you have to remember that the first word on a line in TCL is a command (the rest of the words are arguments to the command) . the text command created a new command which enables you to interact with that text widget. so you call the 'interact with text widget foo' command, which happens to be the path of the text widget, it then uses insert end "Hello world!" to do that operation on itself. hope that made sense - there is more info in the help that comes with TCL -- James Keogh
I am still fuzzy on the steps the interpreter is taking. Tcl is not inherently object-oriented, so thinking of it in terms of an OOP language is probably misleading. The variable substitution may create a command that looks something like:
.myTextWidget insert end "Hello World!"
But what exactly is ".myTextWidget"? It appears that ".myTextWidget" is a dynamically created function, not an object, but my documentation talks about it like it is an object in some places. Is this to sound "in fashion" to attract more Tcl users, or is it really an object?
.myTextWidget is a string, namely the name (actually the "path" in tcl jargon). It's all strings. Actually there may be some gyrations you can go through to get the underlying widget, but in general, with Tcl, you're dealing with names, not references. As an optimization, the compiler generates code that knows about types, widgets, what have you, but the closest analog to tcl is shell script. In the context above, i.e. beginning a line with the widget name, it's interpreted as if it's a function or object (with no references, there's really no difference). You can't go getting a reference to .myTextWidget though, except for its name - Tcl has Call By Name semantics, and oh yes, you can abuse the hell out of those semantics if you try. Ultimately, however, these semantics tend to hinder the language from being able to implement advanced things instead of helping it, as was discovered in Algol Sixty (another language to have Call By Name semantics)
But Tcl is supposed to follow the convention:
command paramString1 paramString2 paramString3 ...
So it tries to execute ".myTextWidget" as a command. Is it actually "registered" as a command? For example, somehow put into the "command/function list"?
yes it is actually a command try this in wish:
button .but -text "test" info commands .*
button creates a new command called .but, .but command acts on a particular button widget. "info" commands list all the commands with the given pattern. -- James Keogh {Did minor syntax editing}
text .myTextWidget
set t .myTextWidget
pack $t
$t insert end "Hello World!"
Let's see if I've got this straight. The first line creates a command called ".myTextWidget". The second line creates a variable "t" that contains the string ".myTextWidget" (for reference convenience). The third line ("pack") sends the string ".myTextWidget" to the "pack" command. The fourth line evaluates internally into:
.myTextWidget insert end "Hello World!"
which executes the command created in line one. In line one, the Tk system creates both a command named ".myTextWidget" and puts the name ".myTextWidget" into an internal list. Line three uses the internal list, not the command (that was also created). Line four executes the command ".myTextWidget" that was created in line one. It is confusing because the Tk system creates two things which can be referenced: a command and an entry in an internal list. These are two different things even though they have the same name. My Tcl book never seemed to clarify this double action, leaving me to scratch my head.
Well, I suppose the Tk system can use the command list as the "internal list" I talked about to achieve the same thing. But, the point is that in some of the commands we are passing a string (widget ID) and in others we are using it as a command.
Regarding this roll-your-own UNTIL loop example from Dynamic Strings Vs Functional:
proc until {condition body} { uplevel 1 $body while {![uplevel 1 [list expr $condition]]} { uplevel 1 $body } }
Why do we need a "list" construct? Why not just:
while {![uplevel 1 $condition]} {...}
These are the tricky parts. "list" can be used to protect from double-evaluation, if $condition for instance contains parts which start with $ we don't want them evaluated by the interpreter but to let the "expr" command deal with them. "list" is generally used for this with "eval" and "uplevel" and similar things -- Kristoffer Lawson
Why don't the "body" uplevel's also need to use "list"?
Because the "body" uplevels only get substituted once. Each time Tcl sees a command, it breaks it up into words (based on spaces and quoting). Then it does one level of expansion in those words (no expansion happens in {} quotes).
So, assume $body == {puts "Hello, world!"}, and $condition == {$a == 1}.
Tcl turns the "uplevel 1 $body" into:
uplevel 1 "puts \"Hello, world!\""
The uplevel then does an eval on the 3rd argument, so the command runs as:
puts "Hello, world!"
Just as we intended it.
The "while" command, on the other hand, gets passed with two words, the condition:
{![uplevel 1 [list expr $condition]]}
And the body:
{ uplevel 1 $body }
The condition gets passed implicitly to the "expr" command (sort of; it won't call your expr if you redefine it). The "expr" command does variable, backslash and [command] substitution on the string. So:
[uplevel 1 [list expr $condition]]
gets expanded recursively:
uplevel 1 [list expr $condition]
then (because [list] quotes its operands):
expr {$a == 1}
gets executed one level up in the stack context.
The {} quoting around the expr argument is required because expr does its own substitution. If you let uplevel parse it as:
uplevel 1 expr $condition
the first level expansion will turn it into:
uplevel 1 expr "$a == 1"
then uplevel itself would substitute:
expr $a == 1
And if, for example, $a == "x y", the expr would fail with a syntax error when it does its own expansion, because it would try to parse:
{x y == 1}
We use TCL where I work to make long-running network servers scriptable. The biggest advantage about TCL is that anyone that has ever written a beggining "intro to C" argc/argv style program knows exactly how to write a TCL command. Then the internal state and various options over behavior are exposed (via a socket attached to the global Tcl Interp) for our operational pleasure.
Richard Suchenwirth : Above it was said that "Tcl is not OO". There is more OO support in the making currently, but for a long while it has been possible to roll one's own OO in pretty simple ways. Example, a Stack "class" without any extra framework needed (both the class and its instances are implemented as namespaces):
namespace eval Stack {variable n 0} ;#-- "class variable"
proc Stack::Stack {} { #-- Constructor variable n set instance [namespace current]::[incr n] namespace eval $instance {variable s {}} ;#-- "member variable" interp alias {} $instance {} ::Stack::do $instance } proc Stack::do {self method args} { #-- Dispatcher with methods upvar #0 ${self}::s s switch -- $method { push {eval lappend s $args} pop { if ![llength $s] {error "stack underflow"} K [lindex $s end] [set s [lrange $s 0 end-1]] } default {error "unknown method $method"} } } proc K {a b} {set a}
Testing in an interactive tclsh:
% set s [Stack::Stack] ;#-- constructor ::Stack::1 ;#-- generated name of a stack instance % $s push hello hello % $s push world hello world % $s pop world % $s pop hello % $s pop stack underflow ;#-- error message on empty stack % namespace delete $s ;#-- destructor
Some Tcl/Tk Links and Tutorials
Official tutorial: www.tcl.tk
Simplified Wrapper And Interface Generator (SWIG) can be used to make calls to Cee Language or Cee Plus Plus code.
See original on c2.com