The Tool Command Language Sucks

17.10.2024

I felt inspired by “Why Does PHP Suck?” to name all the reasons why Tcl sucks despite Tcl already having lost all popularity. If you search for why Tcl sucks, you will only find Reddit posts and an old rant by Richard Stallman from 1994 who loves his competing LISP language which equally sucks for many of the same reasons.

No types, everything pretends to be a string

Internally it has but they are not reliable and you can’t access them. Often it means you will make separate variables to store your type information.

You will have to repeatedly name which kind of variable each string stores before performing the most basic operations on it. Other scripting languages, both object oriented and less object oriented, do it a lot better. string index, lindex and dict get all contain the name of the type they act as whereas other languages simply use var[key].

Comparisons have two sets of operators: one for the strings (eq ne), one for numbers (!= == < >)
Who is to blame? Lack of types.

Is the variable set to a useful value or not? Even in C you can set a pointer to NULL to indicate it. Not in Tcl. You will make a separate variable with a bool or use [info exists] together with unset -nocomplain. Which brings me to the next point.

No boolean

Tcl has no bool. Just 1 and 0. You will mix them up with number variables.

Everything is copied by value

If you need a more complex data structure or want to act as a reference, it will be a string with the name of the other variable and then you’ll have to double dereference it. No, $$var does not work. You have to use [set $var]. If $var is {a} and $a is {b} then it will return {b}.

The advantage is that everything is basically already in a serialized state. You can save and load it without extra work.

No nested functions and lambda in a separate package

Most keywords are commands. Thus proc creates a function in the global scope. They can be renamed or unset but are still separate from variables and their scopes for no apparent reason. That means you can’t nest functions. Lambdas have to be required from a separate package because like many old languages, they can’t break old code by introducing new keywords.

Most things are not strings

Most things are variables or functions and the redistribution of markup from strings to variables and functions will create more work instead of less.

Other languages: foo() (in some without parentheses too) var "str ing"
Tcl: [foo] $var string or {str ing}

Most of the time your function calls will be nested even if you just set a variable: set a [foo]

Bad syntax highlighting

To tools it’s never clear what will be used as code or just text, so the syntax highlighting in your editor code will often mark keywords words in strings and show code as one colored string.

Inconsistent standard library

Compare lset, lindex, dict set, dict get or string map.

Loops more painful than in C

C: for(int i=0;i<10;i++) {}
Tcl: for {set i 0} {$i<10} {incr i} {}

Of course you could always reinvent the wheel for every script since Tcl allows you to write your own control flow functions but you won’t.

It’s metaprogramming capabilities are not that great

While the “everything is a string” philosophy might help somewhat because it means most of what you write will be a string at first, the execution order will still interfere with you making up a completely new language.

You will forget the dollar sign

Having to think about where a variable name retrieves a value and where it will be used as a reference to set a value adds an extra thing to reason about every single time and no matter how much you wrote, you will forget a dollar sign again and again.

Internal encoding mess

Tcl has some Unicode support today but if you load a UTF-8 text file with LF line endings in binary mode and perform any string functions on it, it will destroy your data. Many other languages do not have this problem.

set f [open uni.txt rb]
puts [read $f]
close $f
Hallo Wörld

Ruby:

f = File.open('uni.txt','rb')
puts f.read
f.close
puts IO.binread('uni.txt')
Hallo Wörld
Hallo Wörld

Tk sucks

Tcl can’t be mentioned without its GUI toolkit Tk. If you need anything more than a window, a button and a simple text box then Tk sucks too.

Conclusion:

Tcl is dated and it’s good that it died. It’s cumbersome to read and write. — Oh but Tcl is small and has few rules! — Well Lua is smaller and has less rules if you factor in basic data structures such as hash tables and arrays.

And while, unlike Lua, Tcl can do basic stuff out of the box, for example list files recursively, you are better off using a larger more expressive language in those scenarios.