(2024-10-07) Omnia mea mecum porto (feat. Tcl/Tk)
-------------------------------------------------
Two posts ago, I ranted about the lack of sane and decent desktop GUI
solutions. Now, I must emphasize that whatever I said there only applies to 
compiled programming languages. The domain of desktop GUI scripting hasn't 
been vacant for all these years and hasn't been only limited to JS, VBscript 
(remember that shit?) or some obscure languages from the past like Rexx. 
Popular interpreted programming languages like Python and Perl have been 
having their own bindings to all possible C/C++ GUI frameworks in existence, 
and some languages like Rebol and Red even have self-contained GUI 
facilities. There is, however, one particular language that's simple enough 
to learn it and start rolling functional GUIs in a matter of minutes, 
lightweight and cross-platform enough to be sure your GUIs will run 
anywhere, mature enough to deter any hype riders trying to parasite on it, 
and dynamic enough to ward off "static typing is everything"-type snobs. 
That's why, in the light of a recent new major version release of this 
language and its accompanying graphical toolkit, I'd like to talk about 
Tcl/Tk today.

What's interesting is that despite both of the names being acronyms (Tool
Command Language and Tool Kit respectively), they are just written with only 
the first letter capitalized. Even without Tk, the Tcl language itself is 
interesting on its own: it's built upon the same principles TRAC and REBOL 
were built (even naming TRAC as one of its predecessors on its wiki), but 
it's simpler to grasp than both of them. The language grammar itself is 
defined with the famous Dodekalogue ([1]), sometimes combined into Octalogue 
(the example is given on the same wiki page), but I have built an even 
simpler understanding of what's going on there, and this can be formulated 
in just several sentences.

In Tcl, everything is a string. A string containing a whitespace-separated
sequence of other strings is a list. Unless specified as a literal (within 
double quotes or curly braces), the following three sentences apply to any 
string. Any list is interpreted as a command. A script is a newline- or 
semicolon-separated sequence of commands. Any string can be enclosed in the 
square brackets which causes it to be interpreted as a Tcl script and the 
result substituted in place of the string. If the string starts with $, then 
it gets replaced with the value of the variable whose name is in the string 
(not counting the $). {*} makes each item in a list an individual argument 
of the current command.

That's it, that's the entirety of Tcl language rules. Of course, the
Dodekalogue also contains definitions of valid characters and backslash 
notations and whatnot, but the basic understanding can be as concise as the 
paragraph above. Everything else in the language builds upon the notions of 
strings, lists, list expansion, variable and script substitutions. All 
built-in commands like proc, if, for, foreach, list, dict, array etc have 
nothing special about them and are just commands operating on lists and 
strings. Even the comment operator # is just a command, this is why a space 
is mandatory after it (unless it's a shebang) or a semicolon is mandatory 
before it if you append it to an existing line of code. All this makes Tcl 
even closer to Lisp than one would realize just because of how different 
they seemingly look, although the LISt Processing nature makes Tcl use the 
same prefix notation for all things in the world, even the mathematical 
expressions need the "expr" command if you need to use anything infix. By 
the way, conditional commands call "expr" implicitly, this is why you can 
use infix expressions in the conditions as well.

From the functionality perspective, Tcl is quite a "batteries included"
language even not counting Tk, although the choice of builtin commands and 
packages may seem quite strange. All this because it has a rather unique 
distribution system and plenty of various implementations, sometimes not 
very compatible with each other. Once you have Tcllib ([2]) up and running 
though, I think you're pretty much set ([3]) for 95% of real-life non-GUI 
development scenarios. The GUI part, as you might have guessed, is covered 
by Tk and the Wish (WIndowed SHell) components of the Tcl/Tk distribution. 
And there also is a Starpack system which, although quite outdated (the most 
recent sdx kit file is from 2011), still works pretty well for packing any 
Tcl application into a single binary file. Just make sure to include Metakit 
(mk4tcl), Tcllib and TLS packages into your tclkit binary when building it 
with whatever way you prefer. That's the absolute viable minimum. And, of 
course, don't also forget to include Tk there if you're targeting GUI 
development. On top of that, even the stock Tcl interpreter, tclsh, also 
runs great interactivly as a REPL. Well, it becomes much more usable as a 
REPL provided that you run it via rlwrap, the same way we did back in the 
ed-related post.

Now, let's briefly talk about what Tk can offer us in terms of GUI. While the
Tk widget command syntax is not fully declarative, it's as close as it can 
get to it with zero boilerplate and without sacrificing the flexibility. To 
me, who spends a lot of time with command-line parameters, this syntax is 
much more readable than XML or even HTML, let alone Go/Fyne, C/GTK, C++/Qt 
or other "traditional" GUI toolkits, the only minor inconvenience being 
widget creation and placement consisting of two separate commands. That, 
however, takes place because Tk has three completely unrelated modes of 
widget placement: absolute ("place" command), stacking ("pack" command) and 
grid-like ("grid" command). In real life, you'll mostly find yourself 
combining at least two of these three modes, so you definitely need some 
flexibility when using them, so I don't really regret the creation and 
placement not stuffed into a single line of code. As for the widgets 
themselves, you can find everything you really need for desktop GUI 
programming out there, and the most recent version, Tk 9, even added support 
for host OS printing, notifications and systray. Now you really have no 
excuse to move elsewhere.

The most underappreciated Tk feature though, in my opinion, is bidirectional
data binding out of the box. I reckon this only is available when you use it 
directly with Tcl and not through other language bindings like Tkinter. The 
thing is, you can assign a global variable to any widget that can change its 
parameters, and whenever the user modifies the widget contents (enters text, 
clicks checkboxes, moves sliders etc), the variable automagically changes, 
and vice versa. You don't need to subscribe to any change events or signals, 
or call the value retrieval method or whatever, it just happens by itself. 
This way, the actual widget value always stays in sync with what the user 
sees.

One last thing: contrary to popular beliefs about the bland and obsolete UI
style, Tk is themable. In fact, it is now recommended to create all widgets 
from the ttk (themed Tk) namespace. The non-themed Tk engine is only there 
for backward compatibility with previous versions. Besides several 
preinstalled themes ("default", "alt", "clam" and "classic"), you actually 
have plenty of options with ttk. You can even try to match the host OS style 
if you want to. For instance, for normal OSes, there is a dynamic theme 
called "gtkTtk" ([4]) that you can separately build on your system and have 
it pull whatever GTK theme you're currently using to apply the same style to 
your Tk applications. For other OSes, there are "aqua", "winnative", 
"winxpnative" and "vista" themes to match corresponding native widget 
styles. In your own code, if you're trying to target as many people as 
possible but don't want to include your own theme, you can even write 
something like this in the catch blocks:

# iterate over available platform-dependent themes, apply "clam" if none found
ttk::style theme use clam
catch {ttk::style theme use aqua}
catch {ttk::style theme use winnative}
catch {ttk::style theme use vista}
catch {ttk::style theme use gtkTtk}

Well, I guess this covers the most basic stuff about this incredible
scripting language. I think it deserves more attention than it gets, 
especially in the modern world of total nonsense and bloatware, 
**especially** when it comes to interpreted programming languages and 
graphical frameworks. Some people might argue it's not "enterprise-grade" 
enough despite its history in computing is already quite long. For me 
though, Tcl/Tk still is something really viable for day-to-day desktop 
scripting. I can imagine creating a bunch of small utilities for personal 
use (both online and offline) that could easily fit on a floppy (let alone a 
thumbdrive or a microSD) and travel with me from system to system, from 
environment to environment, not being affected by anything external at all 
as long as the underlying Tcl/Tk version can run there. And with version 9, 
it got even better, although I still will have to stick to 8.6 for some 
time, at least until the 9 gets ported to the AndroWish/undroidwish stack 
and some other places that new versions get ported to much later than the 
upstream ones. Regardless, it still feels exciting to rediscover such a 
language and its capabilities and see a new major version released after a 
long time. Now, I think, desktop GUI development finally got a chance, after 
all.

--- Luxferre ---

[1]: https://wiki.tcl-lang.org/page/Dodekalogue
[2]: https://www.tcl.tk/software/tcllib/
[3]: https://core.tcl-lang.org/tcllib/doc/trunk/embedded/md/toc.md
[4]: https://github.com/Geballin/gtkTtk