What if I told you location exists a language, aliases a full toolkit that:
- Can beryllium utilized to create cross-platform Console and GUI devices aliases apps
- Those GUI apps look autochthonal connected Windows, Linux and macOS
- Supports safe and businesslike threading, non-blocking events and I/O
- Has a mini footprint of astir 100MB pinch astir communal packages installed
- Can nutrient very compact single-file apps for immoderate supported platform
- Is tin of building robust cross-platform Web Apps
- Has existed and been successful usage by immoderate of the largest corporations for complete 3 decades
- Is free and BSD-licensed, truthful you tin do immoderate you want pinch it, aliases to it
Would you judge me?

Foreword
Documenting individual experiences connected fascinating aliases useful topics is what I do. Real-time visualization, 3D simulation, data safety, energy efficiency, aliases moreover input devices, microcontrollers, and fitness — an endless watercourse of ideas for caller studies and exploration is ever connected the ToDo list.
This peculiar taxable turned up to beryllium rather a spot much progressive than I primitively planned. But I promise: each accusation present is what I myself would've loved to person known before I started my Tcl/Tk toolkit learning journey.
I'm penning this agelong aft my honeymoon shape pinch Tcl/Tk ended, and will effort to beryllium arsenic nonsubjective and honorable arsenic possible. Both to you, and to myself. I person thing to "sell" isolated from my individual experience, hoping to amended pass you and simplify the "onboarding" process if you ever determine to springiness Tcl/Tk a try.
Due to its command-centric nature, Tcl is simply a powerful, yet widely misunderstood language. I want to lend to the conversation, aiming to clear up this persistent disorder pinch concise explanations and actual examples. And a spot of flair, of course, to support you entertained.
Please note: We’re skipping the "Programming 101" talk present pinch the presumption that you already person a baseline familiarity pinch wide programming logic. If you cognize your measurement astir an if connection and cognize what function is, you're astir apt fresh to dive consecutive into Tcl-specific structures and syntax. Think of this article arsenic some a "Tcl hands-up and a primer", successful hopes to make you, too, observe and admit this almost 40-year aged exertion that softly powers the world. And possibly person you to effort it for yourself, and dispersed the word.
As for the "About the Author" — if you're interested, please sojourn the "About" page.
Ready? Let's go.
Table Of Contents
- Introduction
- The Why
- Meet Tcl/Tk
- "Heroes Don't Wear Capes"
- The SQLite Connection
- The Anatomy Of Tcl/Tk
- Tcl Basics
- Which One to Use — tclsh Or wish?
- Homoiconicity and the Extreme Flexibility of Tcl
- Basic Demo Of Tcl's Homoiconicity
- A Word for the Experienced Tcl Devs
- Language With a "Different Philosophy of Power"
- Tcl/Tk — Alive and Kicking With Tcl 9.0
- Tk GUI Toolkit
- Tk Cross-Platform GUI Showcase
- Tk — High DPI/4K Ready
- Tk Basics
- Tcl/Tk — Data-Oriented and Event-Driven by Design
- Python, Tkinter and Tk
- Where to Learn Tk
- Tcl 9.0 UI Widget Demos
- Namespaces
- Namespace Ensembles
- The "Everything Is a String" Conundrum
- In Tcl, the Canonical Form of Every Value is simply a String*
- The Same String Can Represent Both Data and a Command
- Native Introspection
- Pure Binary Data? Not a Problem With Tcl!
- Shimmering
- Tracing Shimmering With the "Trace" Command
- The Power of Tcl Dictionaries
- Tcl Arrays
- "Boo! Namespaces Are Better!"
- Global Arrays and Namespace Upvar
- Object-Oriented Programming (OOP) With TclOO
- Why Isn't Tcl More Popular?
- Practical Tcl/Tk Use Case — Decent Espresso
- Why Choose Tcl/Tk?
- Tcl/Tk connected Android With Androwish
- Tcl's Powerful Stackful Coroutines
- Threading successful Tcl
- Threading Demo
- High Memory Efficiency Doesn't Imply Maximum Performance
- Script-Embedded C? Script-Embedded C!
- The "Thin-Client UI" Use Case Example
- Standalone Apps. Starkits and Zipkits
- Tcl 8.6 and Starkits
- Tcl 9 and ZipFS
- What About Tclkits?
- Zipkits and Standalone Apps
- Web-Based Apps pinch Tcl Wapp
- The Fossil Connection
- Wapp.tcl
- More Cool Facts About Tcl/Tk
- "WebAssembly From a Parallel Universe"
- Radical Language Modifications With DSL
- Why Is Doing Math successful Tcl So Awkward?
- Tcl 9.0 — Strict, Honest and Reliable
- Platform-Specific Libraries
- On Nullable Types
- Limitations (and "Quirks")
- The Tcl "Quoting Hell"
- No Garbage Collection
- managed_create
- managed_channel
- No Centralized Package Manager
- Do You Need a Package Manager?
- Mediocre IDE Support
- Limited Multimedia Capabilities
- No Matrix Multiplication
- "Ugh… Tcl Is So Annoying!"
- "Shooting Yourself successful the Other Foot"
- Learning Resources
- Where to Get Tcl/Tk
- Tcl/Tk for Windows
- Tcl/Tk for Linux
- Tcl/Tk for macOS
- Tcl/Tk IDEs
- Afterword
- Useful Links
Introduction
There's plethora of transformative inventions that ended up benefitting humanity arsenic a whole: the Printing Press, Electricity aliases the Transistor. As for the intelligence commons, location are of people the Linux OS, the World Wide Web protocols, and the conception of Public-Key Cryptography. You know, the worldly we mostly return for granted, aliases aren't moreover alert exists.
Tcl, aliases the "Tool Command Language", created and released by John Ousterhout successful 1990, deserves a spot among the top products of the quality mind. Especially erstwhile mixed pinch its amended known graphical personification interface Toolkit — Tk. In 1997 Ousterhout was awarded the ACM Software System Award for Tcl/Tk, an grant fixed to institutions aliases individuals recognized for processing package systems pinch a lasting influence, reflected successful contributions to concepts, successful commercialized acceptance, aliases both.
A bully wide summary of what Tcl is could beryllium recovered successful the project's root codification repository:
Tcl provides a powerful level for creating integration applications that necktie together divers applications, protocols, devices, and frameworks. When paired pinch the Tk toolkit, Tcl provides the fastest and astir powerful measurement to create GUI applications that tally connected PCs, Unix, and macOS. Tcl tin besides beryllium utilized for a assortment of web-related tasks and for creating powerful bid languages for applications.
Tcl is maintained, enhanced, and distributed freely by the Tcl community. Source codification improvement and search of bug reports and characteristic requests return spot astatine core.tcl-lang.org. Tcl/Tk merchandise and mailing database services are hosted by SourceForge pinch the Tcl Developer Xchange hosted astatine www.tcl-lang.org.
Tcl is simply a freely disposable open-source package. You tin do virtually thing you for illustration pinch it, specified arsenic modifying it, redistributing it, and trading it either successful full aliases successful part. See the record "license.terms" for complete information.
Ok, Ok… But what does this each mean, and why should you care?
The Why
Some clip agone I needed to create a cross-platform desktop app pinch a graphical personification interface which would tally connected Windows and Linux (X11/Wayland). Prior to this, I had spent years utilizing AutoHotkey to build mini Windows utilities, for illustration the ones mentioned successful my station connected Alt ♫ Code ♥ Numpad Emulation.
AutoHotkey, specifically v2, is really very capable, and suitable for the improvement of mini GUI tools, not conscionable doing "hotkey-related stuff". With the improved "C-like" syntax it's a pleasance to usage for personification pinch extended inheritance successful JavaScript, PHP, aliases C#.
The only problem? — it's Windows-only. Sure, pinch the Wine compatibility furniture AHK scripts and single-file executables could beryllium tally successful Linux, but there's nary guarantee that each Windows-specific bindings and autochthonal room calls that AutoHotkey relies connected would activity well, aliases astatine all.
I looked astatine different options successful pursuit of a cross-platform GUI model and/or runtime:
- C# pinch WinForms — I considered the .NET ecosystem first, but WinForms remains fundamentally tied to the Windows API. Even pinch modern .NET cross-platform capabilities, achieving a genuinely autochthonal look and consciousness connected Linux aliases macOS requires migrating to MAUI aliases Avalonia, some of which transportation a giant dependency footprint and are a symptom to create "non-enterprise" package with
- Headless Local Web Server (The "Electron-like" approach) — I explored building a instrumentality successful a modern decoupled architecture. A headless backend that sets up a section HTTP API and provides a browser-based frontend. While this achieves an almost 100% compatibility pinch immoderate OS, and is much aliases little considered a "de-facto standard" for modern apps, such complexity is simply a massive overkill for a mini tool. You would request to negociate frontend/backend comms, larboard conflicts, and information overhead conscionable to render a button wrong a browser tab that consumes complete 100MB just to render an quiet page. That's not to opportunity that I wouldn't want to be able to create specified apps. I simply don't want my options to beryllium constricted to only this type of an interface, which besides heavy depends connected really feature-complete the provided browser is
- Go (Golang) pinch Fyne/Gio — while Go produces businesslike binaries, the developer acquisition pinch the UI libs disposable for it quickly descended into "dependency hell." Creating a elemental "Hello World" model pulled successful 40,000 indirect dependencies! And the resulting fixed binary exceeded 30MB, and looked thing for illustration a "native app"
- Qt (via C++ aliases Python) — of people I looked into Qt, "the manufacture standard". Alas, the complexity of its meta-object compiler (MOC), the messiness of signals and slots crossed autochthonal boundaries, and the confusing licensing exemplary (GPL/LGPL vs. Commercial) made it a no-go arsenic well

This is erstwhile I realized that modern "cross-platform" solutions for GUI apps aren't really cross-platform via portability. Instead, they are conscionable packaged environments, carrying each of their crap to wherever they request to run, simply compiled to execute connected the target OS/CPU combo. They don't pat into the autochthonal UI API calls of the OS, and alternatively "draw pixels connected a canvas", which is why specified GUIs seldom look for illustration autochthonal apps.
There had to be a amended measurement to create cross-platform apps and tools!
And that was the infinitesimal when, by serendipity, I recovered a mention of immoderate "Tk toolkit" and decided to look into it, ending up successful a rabbit spread of astonishing discoveries…
Meet Tcl/Tk
Before reference immoderate further, please find 40 minutes to watch this video overview of Tcl. It will get you up to velocity pinch the history and the existent authorities of Tcl, whilst allowing maine to debar dumping each of that into this, already giant, post. Don't sweat excessively overmuch trying to understand each codification examples successful the video, it's capable to get the wide thought of when, why and really Tcl came to be.
Long communicative short, Tcl was meant arsenic a versatile "architectural glue", bonding high-performance compiled codification pinch a flexible, human-readable logic layer.
In software, a "glue language" is simply a programming connection utilized to connect, manage, and automate separate, pre-existing package components that weren't primitively designed to activity together. Rather than building the halfway logic from scratch, "glue" is utilized to:
- Bridge: Connect a high-level personification interface to low-level, high-performance codification (like C++ aliases Rust)
Orchestrate: Control the travel of information betwixt different programs aliases modules
Wrap: Provide a simple, scriptable bid to trigger a analyzable underlying process
"Glue" codification doesn't do the dense computation and alternatively coordinates the components that do.
At first, Tcl was implemented purely arsenic a programming connection pinch its ain high-level interpreter, but Mr. Ousterhout quickly realized that command-line apps had their limitations erstwhile it comes to user interaction, truthful the connection was extended pinch Tk — a cross-platform Graphical User Interface Toolkit. This is why you often spot the "Tcl/Tk" sanction utilized whenever the connection is mentioned, owed to Tk becoming very popular, and 1 of the cardinal reasons to usage Tcl successful the first place.
And since Tk is simply a halfway portion of Tcl, to move a console app into a GUI one, each you request to do is "request" the Tk package. Voia! You tin now build a natively-looking graphical personification interface pinch conscionable a fistful of code.
Yes, it's really that simple, look:

Think of Tk to Tcl narration arsenic what Unity Engine/Unreal Engine are to OpenGL/Vulkan. Most crippled developers put "I'm a Unity developer" into their BIOs, and not "I'm an OpenGL developer." The abstraction furniture (or the Tk hold successful lawsuit of Tcl) is wherever the worth and the organization live, while the underlying connection becomes a specialized "implementation detail". As a ocular effects programmer, you are, of course, expected to astatine slightest understand the basics of the low-level OpenGL aliases Vulcan APIs, but successful each likelihood 80% of your codification will interface pinch the high-level abstracted APIs that the crippled motor you usage provides.
Tcl is besides a semantically elemental language, pinch a very well-written C-implementation. Which is why it's disposable for astir platforms: Windows, Linux, macOS, and Android pinch Termux for CLI Tcl apps, aliases Androwish (Android) and iWish (iOS) mobile apps to tally GUI scripts, pinch a assortment of CPU architectures supported — ARM, x86, RISC-V etc.
There's thing that makes axenic Tcl particularly… special arsenic an interpreted language, compared to its counterparts. Even though location are features that make Tcl guidelines out: its event-driven philosophy, homoiconicity, the "everything is simply a string" attack — features we'll look astatine successful this article — they aren't the only measurement of penning functional software. Actually, the utmost elasticity of Tcl, wherever codification is information and information is code, is what makes it harder to grasp for a modern developer, compared to the much "rigid/structured" languages for illustration JavaScript, Python aliases moreover Lua and Perl.
So then, wherever did and still does Tcl shine?
"Heroes Don't Wear Capes"
Don't worry, I'm not present to show you that "the world of programming has unfairly forgotten astir Tcl, whereas it's the champion point ever!"
You see. Tcl is just… there.
This is astir ironic point astir it. While being 1 of the slightest "popular" languages, for almost 40 years it has been and still is utilized everywhere: powering mission-critical systems, high-end networking hardware, aliases orchestrating transactions of the largest banks. Because Tcl is truthful mini (the halfway is conscionable C), it is embedded successful things we each usage each time without knowing it:
- Git: The git gui and gitk devices that travel pinch each Git installation are Tcl/Tk apps. They look "old" because they still usage the classical Tk widgets, but they are astir "indestructible" and tally connected each OS without dependencies
- FPGAs/Chips: Almost each awesome hardware creation instrumentality (Xilinx, Altera, Cadence) uses Tcl arsenic its superior automation language
- Network Gear: Cisco IOS has a Tcl expert baked into the routers for "Embedded Event Manager" scripts
- Intel, NVIDIA, and AMD: Engineers astatine these companies usage Tcl/Tk to build soul GUIs that power monolithic simulation farms and hardware testers
- Siemens EDA (formerly Mentor Graphics): Their multi-million dollar package suites (like Calibre aliases Virtuoso) usage Tcl arsenic the superior measurement for users to constitute scripts that interact pinch the GUI and the underlying hardware models
- ESA: the European Space Agency and its associated aerospace partners are monolithic users of Tcl/Tk. Major European aerospace laboratories for illustration ESTEC successful the Netherlands, aliases ESOC successful Germany person Tcl/Tk apps moving successful the inheritance of their astir captious operations
Tcl is recovered everyplace wherever reliability, backwards-compatibility and cross-platform compatibility are privilege No1.
Surprised? I judge was. With Tcl/Tk you tin constitute CLI and GUI tools which reliably tally connected everything from a Nuclear Power Plant Terminal to a Raspberry Pi without needing to recompile anything. Wouldn't you emotion to person entree to thing that powerful, for free, nary strings attached?
The SQLite Connection
I should mention that Tcl and SQLite were devised by the aforesaid organization of agleam minds which includes Richard Hipp, the writer of SQLite — the astir deployed and utilized database motor successful the world.
In fact, SQLite was created arsenic a Tcl extension, first! Here's the talk by Hipp himself wherever he explains really SQLite came to be, and really the astir important logic SQLite became truthful successful was its "Tcl past". Very cool talk, I highly urge it.

Some cool facts astir SQLite and Tcl earlier we move on:
- Half of each SQLite tests are written successful Tcl
- The 3rd, existent revision of SQLite, uses dual-ported objects during operation, likewise to Tcl (this conception will beryllium explained later successful the article)
- Tcl is utilized arsenic the "assembler" of the last SQLite code, wherever it takes complete 125 input root C files, does immoderate superior post-processing connected them, and generates the last root codification of complete 200K lines
- SQLite devs are sweet, awesome people. Here's a find I made while skimming done the mixed sqlite.c source codification file:

The Anatomy of Tcl/Tk
Just for illustration almost immoderate different language, Tcl tin beryllium distributed successful galore different ways and bundled pinch each sorts of other packages. The pursuing components dress up a functional Tcl/Tk installation:
- tclsh — arsenic successful "tickel shell" — a "pure" command-line Tcl interpreter. It tin beryllium tally from wrong immoderate console provided by the target OS, and is the main "workhorse" of the language
- wish — arsenic successful "window shell" — Tcl expert that starts up pinch the Tk toolkit package loaded in, meant for processing and moving GUI applications. In Linux and macOS moving the Tcl model ammunition from the console will popular up the default Tk model and commencement the "event loop" successful the inheritance (which we'll screen later), whereas successful Windows it's compiled specifically arsenic a "Windows GUI application"
- Tcl/Tk Libraries — a.k.a. Tcllib and Tklib. A group of modular libraries that should ever beryllium bundled pinch a definite type of Tcl/Tk, to supply support for the out-of-the-core functions and adhd enhanced expertise to the connection and the graphical toolkit. Of course, the contents of the distribution whitethorn vary, but the room usually contains specified captious extensions arsenic http, csv, htmlparse, json, aes and galore much for Tcl itself, and powerful other widgets for Tk: tooltip, aliases widget — a postulation of "mega-widgets" including dateentry (calendar picker), scrolledwindow, and dialog.
- Extensions and Modules (or "Packages") — these widen Tcl pinch caller functionality for illustration threading, resistance and drop, aliases SSL connectivity. Usually they either travel arsenic a portion of a Tcl/Tk distribution, tin beryllium manually downloaded and installed, aliases created and shared betwixt apps arsenic needed. Extensions tin beryllium implemented successful axenic Tcl aliases utilize natively compiled libraries to supply a deeper integration into the target OS's APIs
Such simplicity, arsenic good arsenic a observant attack to version-to-version updates, is what makes Tcl a backwards compatibility champion. A Tcl/Tk book written successful 1995 often runs conscionable good today, particularly if it only relies connected axenic Tcl aliases Tk commands.
All of this is disposable for free acknowledgment to Tcl's BSD-license model. I support mentioning the license, because I want you to "internalize" what this licensing exemplary intends for Tcl and everyone utilizing it:
- Tcl is forever. It's now a communal "good". It "belongs" to everyone
- You tin do almost anything pinch it, aliases to it. And acknowledgment to the geological layers of almost 40 years of package engineering, done by immoderate of the smartest group humanity had to offer, it still provides fantabulous cross-platform desktop GUI integration. You virtually can't inquire for a amended instrumentality improvement kit, particularly considering conscionable really compact it tin be. So compact, that Tcl remained 1 of the astir embeddable instrumentality languages for galore years
- The precocious released Tcl 9.0 is the span that will support that 40-year bequest viable for different 40 years of 64-bit, Unicode-heavy computing. If you see yourself a "power user", there's zero logic not to astatine slightest study the basics of Tcl
Tcl Basics
Contrary to the celebrated belief, Tcl is not conscionable "that weird obsolete bid connection pinch an alien syntax".

Tcl codification looks for illustration words separated by tabs aliases spaces. These words tin either beryllium typed successful directly, aliases generated by various commands and procedures, which spit retired different words and truthful on, while astatine each times, pursuing 1 superior rule:
The first connection is ever a command.
What follows is zero aliases much arguments, forming a complete bid string:
Command Argument Argument Argument …
As for the syntax — the connection follows 12 basal rules, which are often called dodecalugue.
Notable Tcl fundamentals:
- set => Variable Value Set/Get. When called pinch 2 arguments — creates (if needed) and sets a worth for a variable: set myStr "Time and Date". With conscionable 1 argument — gets the worth of the provided variable: set myStr; # => returns "Time and Date"
- $ => Variable Substitution. Replaces a variable's sanction pinch its value. Functionally, the $ is conscionable "syntactic sugar" for the group command, truthful some tin beryllium utilized to resoluteness the worth of a variable
- proc => Procedure Definition. Your basal "function" that executes successful its ain isolated stack for illustration successful astir different programming languages. With a twist: In Tcl, a proc is really a bid that creates different command! So proc sayHi {name} {return "Hi, $name!"} creates a bid sayHi pinch the specified parameters and body
- [] => Command Substitution. Executes a nested book and replaces the brackets pinch the consequence of that script. In C position — it's an inline usability telephone that returns a value
- {} => Grouping (Literal). Group words into a azygous statement without immoderate substitutions. Everything isolated from a \ backslash wrong is treated arsenic a earthy string
- "" => Grouping (With Substitutions). In this grouping $, [], and \ are processed by the expert and their contents are replaced pinch the results of specified processing aliases substitution
- \ => Backslash Substitution. Escapes typical characters aliases continues lines. Useful to break up a agelong bid drawstring into respective lines by virtually escaping a newline character that follows it
- :: => Namespace Scope Operator. Used arsenic a prefix, it references the world scope (e.g., ::myVar). Used arsenic a separator, it defines the way to a bid aliases a adaptable wrong a namespace level (i.e. ::Namespace::Command)
- {*} => Argument Expansion. Treats a azygous database arsenic aggregate abstracted arguments. While information successful Tcl is simply a string, this drawstring tin beryllium interpreted arsenic some a afloat "sentence" and a database of abstracted space-delimited items/words. The second is what's needed, for instance, if you want to walk this information arsenic a list, aliases arsenic a command pinch arguments to Tcl. We'll look astatine its usage cases later
- \n aliases ; => Command Separators. Allows separating commands pinch newlines aliases connected 1 statement pinch a semicolon; truthful you tin constitute and format your scripts nevertheless you want without arbitrary restrictions
- # => Comment. Tcl does let comments, but they should ever commencement astatine a position wherever a bid tin begin. The # and each characters till the extremity of the logical statement are ignored. But that's not the full story: since Tcl checks for comments after parsing the bid into words but before substitutions, braces { must beryllium matched moreover wrong comments. Otherwise Tcl will look for matching closing braces } crossed aggregate lines and won't execute codification aliases propulsion an correction (depending connected the mode of operation) until each closing braces are provided. It's conscionable a quirk of Tcl arsenic a consequence of Tcl's syntactic uniformity, and thing immoderate Tcl dev should beryllium alert of
Couple of examples to show Tcl's bid strings:
# Command 'concat' followed by 2 arguments concat {Hello } "World!" # => returns "Hello World!" # This is besides a bid followed by arguments, pinch the quality that # 'string' is an 'ensemble' command, which acts arsenic a 'command dispatcher'. # It looks astatine the first statement 'range' to find which soul usability to execute # Think of it arsenic a C-like 'String.range(str,start,end)'* aliases Git's 'git perpetrate -m "Done"' string scope "Tcl Programming" 0 2 ; # => returns "Tcl"The drawstring scope illustration highlights the functional quality of "pure Tcl", wherever you don't inquire the drawstring to "trim itself", and alternatively telephone the "range" instrumentality wrong the "string" ensemble to trim a portion of information you gave it. The devices to utilize object-oriented patterns are besides available, thing which we'll screen later successful the article.
Which One to Use — tclsh Or wish?
Both kinds of the Tcl expert support 2 modes of operation:
- Interactive mode — erstwhile you tally tclsh aliases wish without arguments, the expert enters the interactive mode of operation, besides called a Read-Eval-Print Loop (REPL). It executes commands entered by the personification successful the console and prints retired the result. This is akin to really the console ammunition itself functions (bash successful Linux, PowerShell successful Windows), and is intended for experimentation and contiguous feedback. You can technically tally complete scripts successful this mode by either typing them statement by line, pasting the full book into the console, aliases utilizing the root bid to load and construe a record pinch Tcl code, but there's a due measurement to do this:
- Batch mode (Script mode) — meant for execution and automation, and is activated by moving tclsh aliases wish followed by the sanction of a book record — tclsh myscript.tcl. In this mode, the expert sounds the full record from commencement to finish, executes the commands non-interactively, and exits erstwhile the book completes. This is the modular measurement to deploy vanished applications, automate CLI tasks, aliases tally GUI programs, akin to galore different interpreters and CLI apps which judge command-line parameters. Therefore, to tally illustration scripts from this article, prevention those arsenic files and walk them to the interpreter. Like this: wish mortgage_calculator.tkapp
While the record hold is irrelevant to the interpreter, arsenic it only cares astir the existent record contents, it's recommended to instrumentality to modular conventions. Use .tcl for astir of your scripts aliases travel circumstantial situation associations for illustration those provided by the Magicsplat for Windows Tcl/Tk distribution to separate betwixt CLI and GUI entries (.tclapp aliases .tkapp).
As for which expert to spell with — location are immoderate "nuanced" differences betwixt the CLI-focused tclsh and the GUI-oriented wish.
For instance, on Windows, the wish expert is compiled arsenic a "Windows GUI Application", and can't interact pinch the console. In effort to supply 1 to the developer, it presents a "pseudo-console" connected startup, known arsenic "Tk Console". This technically intends that you could tally everything pinch wish, but location is simply a important catch: the UI and the book logic unrecorded connected the aforesaid thread. If a book performs dense lifting, it "starves" the GUI of the idle clip it needs to redraw. This intends your puts statements are efficaciously queued, and won't really show successful the pseudo-console until the book pauses aliases finishes, leaving the console model "frozen" successful the meantime.
Conversely, the tclsh expert is compiled arsenic a "Windows Console Application", and truthful it integrates into the console (cmd aliases PowerShell). So if you effort to tally the aforesaid book pinch tclsh, you will spot each of those puts messages logged into the console 1 aft the other, arsenic Windows does supply command-line apps pinch the modular input and output channels — stdin and stdout. That's why the strategy console tin much reliably person and show information sent to those channels in-between dense "thread-blocking" calculation loops.
In simpler terms: while learning Tcl/Tk conscionable usage tclsh:
- It will ever supply your scripts pinch the modular input and output channels regardless of the OS, beryllium it Windows, Linux aliases macOS, streamlining cross-platform instrumentality development
- For GUI instrumentality improvement — tclsh will automatically load successful the Tk GUI package and create the default top-level model if it's ever requested successful a book pinch package require Tk, to fto you believe building GUI apps erstwhile you're ready
- Same for erstwhile learning Tcl's event loop — simply load the Tk package to rotation up the inheritance arena loop to play pinch record and transmission events correct wrong an interactive session. While this is technically imaginable successful the command-line tclsh pinch a civilization event-driven REPL implementation, it perfectly isn't thing you should interest astir correct away
Homoiconicity and the Extreme Flexibility of Tcl
Since Tcl codification consists of "just words", for the connection itself this intends that Tcl doesn't thrust a wall betwixt code and data, and allows you to determine really to construe a definite string. You tin conscionable dainty it arsenic data, for illustration a paragraph of text, aliases a database of items, aliases modify if connected the alert and walk the aforesaid drawstring to the expert arsenic a bid drawstring to execute arsenic code.
Such conception is called "Homoiconicity".
In machine science, a connection is homoiconic (from the Greek homo- "same" and eikon "image") erstwhile the programme building is identical to its information structure. In Tcl’s case, both the codification and the information are strings, and location is nary favoritism betwixt "keywords" and "functions".

Now, retrieve really successful the preamble I promised full honesty? Well, present it goes:
In half of the books connected Tcl this "homoiconicity" would astatine this constituent beryllium praised arsenic thing amazing, ingenious. Or an outrageous declare would beryllium made like: "This changes everything!!!11"
This is highly debatable. Claiming data-as-code is simply a use is for illustration building a location wherever each ceramic is simply a imaginable instrumentality of dynamite. It’s an architectural nightmare for anyone who values structural integrity and security.
Here's a elemental demo of really a drawstring tin beryllium interpreted arsenic information and/or code, and really easy it is to make a mistake:
# Set aliases person a drawstring pinch data set userinput {puts "Hello World!"} # Treat it arsenic a database of items, and count them llength $userinput; # => returns '2', those being 'puts' and {Hello World!} # Now construe the aforesaid drawstring arsenic a complete *command string* instead # Expand the contents pinch {*} to make Tcl construe grouped words arsenic database items # This measurement Tcl will spot a abstracted bid 'puts' pinch an statement 'Hello World!' {*}$userinput; # => returns "Hello World!" # Now let's 'forget' to decently group information pinch quotes # A elemental typo successful 'data' becomes a CRASH successful 'code' set userinput {puts Hello World!} # Now 'puts' will spot 2 arguments, treating the first 1 arsenic a transmission name, # and effort to nonstop the drawstring 'World!' to the "Hello" channel {*}$userinput; # => ERROR: tin not find transmission named "Hello"Note really the drawstring contents are "just words" separated pinch an quiet space, meaning immoderate first connection will beryllium treated arsenic a bid call, followed by immoderate other seen arsenic arguments. You request to beryllium observant and decently group arguments pinch spaces wrong a bid string. This is 1 of the reasons why much structured languages mostly took complete arsenic the clip passed. Since they intelligibly isolate programme codification from data, you request to spell retired of your measurement to execute thing arsenic valid code, by deliberately formatting it arsenic a usability call: console.log("Hello World!"). Such rigidity is what besides makes robust fixed codification study possible.
Besides, tin you construe a random drawstring arsenic a usability in JavaScript? — Of course! You'd usage eval for that:
let cmd = 'console.log("Hello World!")'; eval(cmd); // => logs "Hello World!"Experienced developers are already wincing astatine the mention of eval arsenic they cognize conscionable really vulnerable it is to see "dynamic" codification information a "good idea".
In reality, homoiconicity is simply a "side-effect" of Tcl's syntax. It's not immoderate "magical" aliases "ingenious" feature. If your superior norm is whitespace-separated commands and arguments, and your only grouping mechanisms are braces {} and quotes "", your codification is indistinguishable from a database of words. That's it. It's a structural inevitability.
So no, Tcl is not "amazing" arsenic a consequence of its homoiconic nature. Instead, it's extremely malleable.
Therefore, let's spell pinch a "safer" and much realistic illustration wherever Tcl's homoiconicity tin beryllium of use.
Basic Demo of Tcl's Homoiconicity
In C# aliases JavaScript while is simply a reserved keyword baked into the compiler’s grammar. But since everything successful Tcl follows the command argument argument ... pattern, moreover specified seemingly "core" commands arsenic if, for, aliases while are conscionable that — commands, nary different from a bid you would constitute to people matter aliases move a file.
Let's return look astatine the while loop successful Tcl:
set x 0 while {$x < 3} { puts "Iteration $x" incr x }Here, while is conscionable a command followed by 2 arguments:
- {$x < 3} — the "condition" argument, that gets tested connected each iteration
- {puts "Iteration $x" ; incr x} — the book to tally connected each loop iteration
As of the basal syntax rules, curly braces {} show the expert to dainty the contents arsenic a literal string, and conscionable walk them connected to the while command. Therefore if, for, while and different commands simply judge {some data} arsenic arguments to run on. Which, again, proves that successful Tcl moreover "default keywords" are not really keywords, but commands, implemented by the Tcl connection developers conscionable for illustration immoderate Tcl book developer could.
In contrast, successful JS/Python/C# and galore C-like languages, location is simply a "hard wall" betwixt the codification you constitute (Syntax) and the strings/numbers your codification operates connected (Data). There, if, while, and for are so keywords. For instance, if you look astatine the JS root codification for the V8 motor (Chrome/Node.js), the while keyword is handled by a monolithic C++ parser and a authorities machine. It's "static" codification fixed to you to usage "as is".
OK then… What if I want to make my own type of a while loop?
To make a civilization while successful JavaScript you person to wrap your codification successful functions to forestall them from executing immediately. This is because In JavaScript x < 10 is an expression. If you don't wrap it into an arrow usability () =>, it evaluates to existent aliases mendacious before it moreover reaches your function. Therefore it's possible, but won't beryllium pretty.
Look astatine this mess:
function myWhile(conditionFn, bodyFn) { while(conditionFn()) { bodyFn(); } } // You MUST usage "() =>" (Lambdas) to "freeze" the code myWhile(() => x < 10, () => { console.log(x); x++; });Meanwhile, successful Tcl, you could constitute your ain process successful a very straightforward manner. It could return 2 strings provided wrong the {curly braces} and run connected them.
You conscionable request to make judge not to tally the provided information book wrong the isolated scope of your caller procedure, and usage the uplevel bid to "reach" the variables specified wrong those curly braces from wrong the scope of the process that called your implementation of the loop. Which is fundamentally the aforesaid measurement it's done successful the "default" if, for, while and galore different Tcl commands.
For example, let's constitute a repetition bid which accepts 2 strings, and besides provides 2 modes of operation: until and while:
proc repetition {body mode condition} { while {1} { # IMPORTANT! Execute the assemblage successful the *caller's* scope uplevel 1 $body # Now measure the information drawstring successful the caller's scope arsenic well group consequence [uplevel 1 [list expr $condition]] # Switch logic based connected the 'mode' provided, easy to extend move -exact -- $mode { "until" {if {$result} { break }} "while" {if {!$result} { break }} default { return -code correction "Unknown usability '$mode': must beryllium 'until' aliases 'while'" } } } } # Usage: "until" set x 0; puts "Testing 'until' x >= 3:" repeat {puts "x is $x"; incr x} until {$x >= 3} # Usage: "while" set y 0; puts "\nTesting 'while' y little than 3" repeat {puts "y is $y"; incr y} while {$y < 3}Voia! Here's your caller fancy type of the "default" while bid successful Tcl.
This is akin to really the banal if bid runs nether the hood, conscionable pinch a different bid aliases arguments:
set x 7 # bid statement argument statement argument if {$x > 10} {puts "High"} other {puts "Low"} # You tin moreover make 'else' dynamically, since it's conscionable an statement string if {$x > 10} {puts "High"} [string feline "el" "se"] {puts "Low"}Even return is not a reserved keyword. It's not hard-wired into the compiler to halt execution for illustration it does successful C++, C#, JS and galore others — alternatively it's conscionable a command that straight communicates pinch the expert via integer codes:
- 0 (TCL_OK)
- 1 (TCL_ERROR)
- 2 (TCL_RETURN)
- 3 (TCL_BREAK)
- 4 (TCL_CONTINUE)
And you're free to specify immoderate of these codes manually erstwhile calling the return command, changing Tcl's behaviour arsenic needed.
A Word for the Experienced Tcl Devs

This short conception is aimed astatine the knowledgeable Tcl devs who ended up reference this article and mightiness beryllium fuming pinch righteous rage aft reference my disapproval of Tcl's homoiconicity. Beginners whitethorn safely skip it.
Yes, I'm alert that Tcl's safe interpreters exist. I besides cognize that you should [list] your callbacks. Lastly, successful Cisco routers, for instance, EEM (Embedded Event Manager) policies are fundamentally lists of strings, and they activity good pinch the string-based Tcl. But arsenic acold arsenic I know, Cisco went pinch Tcl chiefly because it was a lightweight, string-based motor that fto them dainty user-provided matter arsenic hardware-level logic, fitting perfectly into the strict representation constraints of the 90s hardware. Well that, and besides Expect. Those who cognize — they know. So speech from Tcl, location simply weren't galore different options to take from arsenic "glue" for implementing elastic personification entree to strategy APIs.
Cisco's reliance connected Tcl has evolved into a authorities of attraction of a bequest method debt. They are successful the mediate of a monolithic architectural pivot, and the threat of Tcl's string-based quality wherever "anything whitethorn beryllium anything" is precisely why they are looking for alternatives. Instead of having Tcl scripts interact straight pinch the C-based power plane, Cisco now encourages moving Python 3 wrong a Guest Shell container, arsenic it doesn't dainty strings arsenic hardware-level logic by default and alternatively interacts pinch the router via structured APIs (like cli.execute() aliases NETCONF/YANG models) alternatively than earthy drawstring evaluation.
Tcl's homoiconicity was a creation shortcut that made Tcl easy to embed successful the 90s but makes it little favorable today, erstwhile much and much attraction is connected security, moreover if it comes astatine a value of much rigid rules and structures. The world of modern networking is conscionable measurement excessively immense and chaotic to disregard the risks that wide usage of a homoiconic connection would carry. Modern CPUs and fixed analyzers are now acold much tin of optimizing and securing rigidly system languages. These devices trim the cognitive load connected developers by detecting errors and vulnerabilities overmuch earlier successful the improvement cycle. It's a information nett that a connection arsenic fluid arsenic Tcl simply can't provide.
All successful all, I'm not bashing Tcl for its utmost flexibility, I'm being honorable and pragmatic astir the imaginable risks arsenic a consequence of an architecture wherever information is semantically indistinguishable from code.
And if you still disagree, please fto maine cognize what it is I'm incorrect about, directly. Thank you.
Language With a "Different Philosophy Of Power"
Guess what — you don't person to dainty information arsenic code, unless you take to! You tin create devices and GUIs pinch conscionable the "default" group of Tcl/Tk commands and extensions. But the doorway is ever open. In C#, JavaScript, Python and the like, specified powerfulness belongs to the compiler squad astatine Microsoft, Mozilla aliases the Python Software Foundation. In Tcl, the powerfulness belongs to whoever is penning a script.
Tcl's Homoiconicity will look "weird" and sometimes confusing erstwhile you commencement your learning travel pinch it, particularly if you person extended acquisition pinch much "rigid" aliases "structured" languages. But complete time, you'll understand it better, and mightiness moreover usage it to your benefit. For illustration — to create full sets of civilization commands, operators and programme flows successful the shape of Domain Specific Languages (DSLs).
As you get much knowledgeable pinch Tcl, you'll gradually modulation into a "Tcl way" of thinking, and people steer much towards the first intent of this tool bid language: from instrumentality development, scripting and automation, to "gluing" exertion components together. Here Tcl/Tk genuinely shines, providing a portable, native-looking interface to thief negociate high-performance, natively compiled binaries doing the dense lifting. We’ll research really to span these 2 worlds later successful this article.
Tcl/Tk — Alive and Kicking With Tcl 9.0
Although not very "popular", Tcl/Tk is still actively developed by the Tcl/Tk Core Development Team. On Nov 13, 2025, 12 years aft the merchandise Tcl 8.6, they presented a caller awesome type of Tcl — Tcl/Tk 9.0.
It's a genuinely milestone release, arsenic it made Tcl a afloat 64-bit-aware language. It mightiness look puzzling and moreover silly why this wasn't done sooner, but you tin already conjecture what specified a modulation brings: it changes the pointer size from 32 to 64 bit. Meaning, immoderate of the existing Tcl scripts, particularly those embedding axenic C codification (critcl package) could either extremity moving aliases grounds unexpected behavior. And since Tcl historically provided outstanding backwards-compatibility, the halfway squad of devs had to beryllium very observant while implementing and introducing specified an update, while maintaining compatibility pinch arsenic overmuch of the existing, sometimes decades-old code-base, arsenic possible. This takes time.
With this update Tcl is now moreover much "modern" and robust than ever:
- Supports the afloat Unicode codification constituent range. Tcl was already well-known for its superb expertise for localization (or "internationalization"), and the caller merchandise streamlines its Unicode backbone, further improving Tcl's robust, industry-leading support for multi-language tools:

- Tcl now natively supports the ZipFS virtual filesystem. After years of having to trust connected 3rd-party hacks and products to create self-sufficient apps aliases bundle information pinch scripts, you tin now connect a zip record to a book and equine it arsenic a virtual entree constituent to entree the files and the directories inside. We'll talk much astir this important characteristic successful the Tcl 9 And ZipFS section
- Tcl/Tk 9.0 importantly improved the graphical personification interface (GUI) capabilities by introducing support for scalable vector graphics (SVG), arsenic good arsenic vastly improving Tk's precocious DPI show awareness, thing I will show successful the pursuing section. Also, Tk 9.0 added autochthonal Desktop Notification support and amended integration pinch strategy themes for illustration Dark Mode connected Windows/macOS
- Octal Literal Sanitization by default — successful Tcl 8.x, 010 was interpreted arsenic octal (8), which led to endless "WTF is pinch this math?!" bugs erstwhile handling starring zeros, for illustration zip codes aliases dates. Tcl 9 uses the 0o prefix for octals, for illustration 0o10. A starring zero nary longer changes the guidelines of the number. If your bequest scripts trust connected the aged behavior, they will now dainty 010 arsenic decimal 10, truthful that's 1 of those very welcome, but perchance backward-compatibility-breaking changes
- Overall much strict curen of data. Tcl 8.x could sometimes effort and "be smart" erstwhile reference matter files by silently substituting invalid byte sequences pinch replacement characters aliases falling backmost to ISO-8859-1 (Latin-1) erstwhile it encountered encoding errors. While this prevented scripts from crashing, it often led to unexpected information corruption that was astir intolerable to debug erstwhile the information was saved. In Tcl 9.0, this behaviour has been replaced by a general Encoding Profile system. So while successful Tcl 8.x, if you publication a record arsenic UTF-8 that contained a stray non-UTF-8 byte, Tcl would often conscionable transportation on, Tcl 9.0's default floor plan is now strict. If Tcl encounters an invalid byte series for the specified encoding, it will instantly propulsion an error, which is paramount for cross-platform instrumentality development. I cognize this, because for the past twelvemonth I had to woody pinch tons of arbitrarily-encoded files, and americium grateful that Tcl fto maine cognize correct distant erstwhile thing was incorrect pinch a record I was moving with. A record that C# and Python deemed perfectly good BTW, arsenic "being smart" is simply a characteristic of both. Tl;dr: Tcl 8.x assumed the programmer wanted the programme to support moving astatine each costs. Tcl 9.0 assumes the programmer wants the information to beryllium correct astatine each costs.
While you don't person to usage Tcl 9.0 and tin still create CLI and GUI devices pinch the Tcl 8.6 branch, I highly urge either starting retired with, aliases updating to the 9.0 version. You get tons of benefits, further fail-safes successful regards to information processing, and a vastly superior Tk's UI ability.
Finally, the charismatic Tcl/Tk root codification isn't moreover the only measurement to usage the language. Other devs connection their ain interpreters pinch the slightest magnitude of C-code possible:
- Jim TCL is simply a small-footprint implementation of the Tcl programming connection that implements astir Tcl features successful a very compact expert of astir 100-200kB successful size. All of that — pinch little than 10k of C code and plenty of extensions available
- Or, astatine the extremes, we tin find projects for illustration Picol — wherever with little than 1000 lines of C code the writer was capable to replicate Tcl's syntax and immoderate of the cardinal Tcl commands

OK, OK… Enough talk astir "pure Tcl".
As absorbing a taxable arsenic Tcl is, there's one characteristic of the Tcl/Tk toolkit that's truthful ubiquitous and versatile, that group often hide that it has immoderate narration to Tcl astatine all:
Let's not hit astir the bush and get to the cardinal logic why Tcl is particularly applicable today, conscionable arsenic it's been for the past decades — the Tk GUI Toolkit. Soon aft the first nationalist merchandise of Tcl, John Ousterhout realized that the world of compute was quickly moving towards much user-friendly graphical personification interfaces. He besides knew that location existed respective competing operating systems, each — pinch its ain implementation of the UI APIs. This led to the creation of Tk, which became an basal portion of Tcl arsenic we cognize it. So essential, that the "general personality name" of the connection itself was changed to Tcl/Tk arsenic a result.
Over time, Mr. Ousterhout chose to move Tk into a cross-platform toolkit, making judge the aforesaid Tcl codification could beryllium utilized (with minimal changes) to create GUIs connected different platforms.

Tk is the original "Write Once, Run Anywhere" UI. Long earlier Electron was consuming each the RAM successful the world, Tk was providing a lightweight span betwixt Windows, macOS, and X11 pinch a footprint that makes an quiet C# binary look bloated. Due to this fact, Tk has been so wide successful master circles (automotive, spot industry, astronomy) that immoderate effort to estimate really galore interfaces were implemented successful Tk, arsenic good arsenic are still used, is wholly futile.

In fact, Tk became truthful powerful and portable, that languages for illustration Python, Perl, and Ruby really adopted it arsenic their modular library, for illustration Python's tkinter we'll look astatine later.
Tk's cross-platform support isn't moreover its slayer feature. Development speed is. You tin build a functional, cross-platform dashboard successful 100 lines of Tcl that would return 1000 lines of C++/Qt.
However, if you only look astatine the screenshots of the decades-old Tk interfaces, you mightiness get an belief that Tk GUI is ugly. Indeed, for 20 years since its inception, Tk didn't travel pinch ttk — Themed group of Tk widgets. It looked for illustration Windows 3.11 aliases 95 UI connected each platform: gray, boxy, pinch very spartan decoration options. So complete clip astir developers moved to the web aliases Qt earlier Tk yet sewage native-looking. They haven't looked backmost to spot that it’s fixed.
It has agelong been fixed, and past some!
For instance, did you cognize Tk supports themes?
Out of these, alt, default, clam and classical are ever present, pinch other themes disposable depending connected the OS and the packages offered by your Tcl/Tk distribution. In astir cases you'll want to usage the "default" (not "classic") theme, arsenic it will look closest to autochthonal connected each of the platforms supported by Tk via nonstop bindings into the Windows UI DLLs and X11/Cocoa's APIs. You tin besides create your ain themes aliases download community-made ones if you like.
With the preamble of the ttk widget set, 1 tin beautiful easy larboard aged Tk apps to update their looks. Here's an illustration of a very aged tool that uses a "classic" Tk widget — treectrl moving without immoderate modifications utilizing Tcl/Tk 9.0:

And here's the aforesaid instrumentality aft I ported it to usage a Themed Tk widget — ttk::treeview:

Contrary to the stereotype, Tk apps tin look almost arsenic autochthonal as, well… native ones, whilst still maintaining their cross-platform compatibility.
Allow maine to demonstrate.
Tk Cross-Platform GUI Showcase
While exploring the Tcl browser plugin demos, I stumbled upon a "mortgage calculator applet". It was past updated successful 1996, and yet still ran perfectly pinch Tcl 9.0.3. Of course, being that old, it relied connected bequest techniques: canvas size was hard-coded, arsenic were immoderate worth thresholds, the GUI utilized the battalion geometry head alternatively of grid, and relied connected the classical Tk widget group for the inputs and the button, which look for illustration they came consecutive from 1996, arsenic well.
Here it is successful its afloat glory:

I decided that updating it could go a bully Tcl/Tk learning exercise. One point led to different and I ended up rewriting it almost wholly arsenic a "modernized" version, which contains a bunch of changes:
- Uses the grid geometry manager
- Allows specifying a down payment
- Implements extended input validation and re-fitting
- Supports precocious DPI screens, including Android mobiles
- Implements rate-limited canvas resize
- Has 100% cross-platform accordant UI layout
- Uses namespaces and arrays alternatively of a messy, level adaptable structure
- Comes pinch plentifulness of comments to explicate random things
💾 Download the latest type here to cheque retired really I implemented each of the above.
Stripped of each comments and quiet lines, the original type would incorporate 188 lines of code. Mine comes adjacent astatine 250 lines, which I'd opportunity is fair, considering each the caller features and updates.
Here it is moving connected Windows 10:

And connected Linux Mint MATE 22.2:

Looks bully connected my Android tablet successful Androwish successful image predisposition (scaled down from 1600x2560):

And successful scenery predisposition arsenic well:

Also fits my constrictive Android telephone surface (scaled down from 1080x2340):

All of the screenshots were taken by moving the aforesaid script.
Hard to believe, right? Here's a decades-old time- and battle-tested, genuinely cross-platform, free and open-source GUI framework, waiting to beryllium used, while little and little developers are alert of it.
And present immoderate Tcl and Tk scripts are moving connected a miniature Arm-based single-board machine pinch an Arch Linux-based OS, presented connected a 5-inch 1024x600 screen:

But wait, there's more!
Tk — High DPI/4K Ready
Tcl/Tk 9.0 has importantly improved precocious DPI support compared to the erstwhile awesome releases. Now, the modern ttk:: widget room wholly shatters the stereotype of Tk apps looking "old" and "ugly", aliases not tin of adapting to correctly show connected precocious DPI screens, for illustration 4K ones.
Here's the aforesaid "modernized" Mortgage Calculator book moving connected a Windows 10 instrumentality pinch a 1080p display, pinch strategy scaling group to 100%, which corresponds to the default 96 DPI pixel density:

And present it is successful Windows 10 pinch 150% scaling, which corresponds to 144 DPI:

Did you really deliberation I'd hide astir macOS? Here's the aforesaid book moving connected a precocious DPI surface Mac. Also shows conscionable really "opinionated" macOS is, refusing to standard the fastener vertically, and really Tcl/Tk tin easy woody pinch this, neatly placing the fastener widget successful the middle:

With Tcl/Tk 9.0 I was capable to execute an almost cleanable 1-to-1 scaling successful some cases! With a due attack to UI improvement you, too, tin make Tk GUIs support their look crossed galore devices sloppy of the surface pixel density.
In fact, you should ever for illustration sticking to relative units for fonts, widget sizes, paddings and offsets, different seeing thing for illustration this connected a high DPI surface will beryllium almost guaranteed:

Tk Basics
Having seen beautiful pictures, let's dive deeper nether the hood and spot really Tk UIs are built.
Being cross-platform, Tk UI kit doesn't expose native Win32 DLL calls aliases Linux X11/macOS Cocoa soul workings, and truthful immoderate relationship that needs to hap needs to beryllium programmed successful explicitly. Thankfully, Tk provides high-level abstractions, besides known arsenic "widgets", which do astir of the heavy-lifting, while exposing the basal controls for the programmers. The measurement these widgets autumn into the Tcl architecture is besides highly elegant — each Tk widget exists arsenic a command! They aren't fixed elements, but successful a measurement "objects" pinch their ain "methods" which they supply to beryllium configured.
Most importantly, widgets mostly don't grounds hard-coded behaviors and expect developers to specify bindings and callbacks. And since Tk UI programming is event-based, conscionable for illustration autochthonal OS UIs are, by learning Tcl/Tk you'll get a amended knowing of really graphical personification interface programming is done nether the hood successful desktop operating systems.
As for Tk basics, location already exists a Tk-related assets with an fantabulous tutorial. Please, do caput complete to TkDocs.com and spell done astatine slightest the first 4 chapters, to study astir the circumstantial preceding "." (dot) naming strategy utilized by Tk, and really windows and widgets are system overall. This is beautiful overmuch required if you want to understand the remainder of the examples connected Tk successful this article.

Long communicative short: to make thing successful your UI do anything, aliases to respond to events and inputs, for illustration changing states of UI elements, scrolling pinch a rodent scroll/touchpad, aliases resizing widgets together pinch the window, you request to specify specified behaviors and bind widgets and events pinch callbacks. Most of these are shared behaviors and are good documented, pinch plentifulness of demos and samples available.
Speaking of demos, let's inspect this illustration from Tcl 9.0.3 widget demos (here's really to entree them):

Here we spot a ttk::treeview widget pinch 2 scrollbars (comments added for clarity):
# 'treeview' widget created arsenic a kid constituent of .mclist window ttk::treeview .mclist.tree -columns {country superior currency} -show headings \ -yscroll [list .mclist.vsb set] -xscroll [list .mclist.hsb set] # Two scrollbars simply created arsenic siblings of the treeview widget ttk::scrollbar .mclist.vsb -orient vertical -command [list .mclist.tree yview] ttk::scrollbar .mclist.hsb -orient horizontal -command [list .mclist.tree xview]The first study you tin make is that the 2 scrollbars aren't "embedded" into the treeview widget. The writer of the demo chose to make them siblings of the widget, arsenic it's commonly done. Meaning that the treeview widget tin beryllium equipped pinch conscionable one, aliases moreover nary scrollbars if needed, and alternatively beryllium controlled done different means, for illustration keyboard arrow keys aliases purely done logic (think a hunt usability that auto-scrolls to a result). But arsenic soon arsenic further power elements are added to the window, they request to someway beryllium capable to interact pinch the scrollable widgets.
Let's look astatine really each of this useful nether the hood:
- Widget to Scrollbar: When the position wrong the treeview changes via rodent instrumentality aliases information insertion, the widget executes the bid drawstring assigned to -yscroll, successful this lawsuit — .mclist.vsb set. It appends 2 fractions representing the visible range, commanding the scrollbar to update its slider's size and position. And of course, .mclist.vsb itself is simply a command, created by the ttk::treeview aliases ttk::scrollbar, which are commands arsenic well
- Scrollbar to Widget: When a personification interacts pinch the scrollbar, the scrollbar besides receives the bid prefix assigned to its -command option. For vertical scrolling it's .mclist.tree yview. Depending connected really the personification interacted pinch the scrollbar, the scrollbar supplies the yview bid pinch the due scrolling arguments, for illustration moveto 0.5 aliases scroll 1 units. In different words, the scrollbar takes that .mclist.tree yview bid and appends arguments to it, for illustration so: lappend command "moveto" 0.5, ending up pinch the complete bid drawstring {.mclist.tree yview "moveto" 0.5}, which it past executes (in a measurement akin to the eval $command) instructing the treeview to displacement its soul coordinate strategy to a caller position
See really Tcl hides nothing from you? It virtually builds up bid strings, and executes them arsenic instructed. This intends that you tin some replicate "native" model controls, arsenic good arsenic make immoderate elements power immoderate number of different elements, while building a portable, cross-platform GUI. Most modern frameworks for illustration Electron, Flutter aliases SwiftUI hide this "handshaking" logic down Reactive State aliases Data Binding, acting for illustration "black boxes", stifling maturation of developers and starring to each sorts of difficult to find bugs and capacity issues. Not to opportunity that you won't person bugs successful your Tcl scripts, but successful the immense mostly of cases those will beryllium wholly your fault, easy to diagnose and fix. Very refreshing!
Also, statement really the -command callbacks are passed arsenic lists successful the example. This is considered bully believe erstwhile building up callbacks for Tk widgets aliases Tcl commands for illustration eval, after, bind, subst, because [list …] items are "atomic". In Tcl, this prevents word-splitting bugs if callback arguments ever hap to incorporate spaces aliases typical characters. In languages for illustration JS, you walk a usability reference pinch arguments: setTimeout(myFunc, 1000). In Tcl, you walk a string that the expert will parse and execute astatine a later time. You request to guarantee that that astatine the clip of the call, that drawstring would incorporate a valid bid string, wherever the bid and each its arguments are decently grouped. A database will automatically enclose contents pinch whitespaces into {} braces, maintaining the required order and the number of items. It's 1 of those "Tcl'isms" you'll get utilized to arsenic the clip passes, and is the aforementioned "side-effect" of the "Everything is simply a String" principle, and the wide Tcl's syntax, wherever commands and arguments are separated by whitespace.
To understand "callback listing" better, effort moving these commands and observe the results:
# Load successful Tk to guarantee the Tcl arena loop is running package require Tk # Create information pinch spaces set information "Data pinch spaces!" # Set up a callback that FAILS, because $data is replaced pinch the value, # and the resulting bid and arguments are NOT decently grouped after 1000 "puts $data"; # Sets up a callback 'puts Data pinch spaces!' # ERROR: tin not find transmission named "Data" # Callback that SUCCEEDS, because [list] will automatically brace the string # that contains spaces aliases typical chars, generating a valid bid call after 1000 [list puts $data]; # Sets up a callback 'puts {Data pinch spaces!}' # SUCCESS: "Data pinch spaces!"As for the widget interaction, specified rigid coupling useful good for closely-related elements (like a scrollbar successful a window, aliases an array of checkboxes connected a canvas). However, to group up connection betwixt systems you should look for different approach. Building UIs pinch abstracted windows and contexts pinch tight couplings via -command parameters is OK for mini tools aliases while learning Tk. As soon arsenic your programme grows beyond 2-3 windows, you'll announcement really managing those limitations becomes much difficult.
Thankfully, there's an elegant and robust solution.
Tcl/Tk — Data-Oriented and Event-Driven by Design

Here's wherever Tcl/Tk really shines, and why I chose to walk almost half a twelvemonth penning this article to show really the toolkit could go a "manager's champion friend" arsenic a powerful, cross-platform programmatic "glue" to robustly necktie together systems and components.
In the codification artifact supra I mentioned immoderate "event loop". It's a typical power system that manages the processing of tasks and outer inputs successful a non-blocking, single-threaded environment.

Simply speaking, Tcl's arena loop continuously processes events, pulled from the arena queue, usually dozens of times a second. It watches for rodent aliases keyboard events, invoking bid callbacks and arena bindings arsenic needed. Most importantly, arena loop useful pinch queues — either you, aliases immoderate commands successful your code, aliases the OS itself tin queue definite actions arsenic events aliases callbacks, which the arena queue past processes successful an orderly manner.
To understand the specifics of Tcl's arena loop implementation refer to TkDocs, arsenic they supply a good illustrated explanation.
Event loop is simply a portion of "pure Tcl", it's not thing applicable only to Tk, but Tk cannot beryllium without the arena loop. That's why each clip you load successful the Tk package, the arena loop is started successful the inheritance for you.
For developers this intends that pinch Tcl/Tk you tin some respond to strategy events (key presses, model size changes, rodent clicks and hovers etc.), arsenic good arsenic generate civilization "virtual events" pinch data! Think of it arsenic sending an letter cover pinch a connection to subscribers of a definite event. The contents of that envelope? — You get to some specify and construe arsenic you please.

Here we make an arena <<UI:Request:Submit>> consecutive "into" the .mywidget widget (you whitethorn besides deliberation of it arsenic a "channel") and walk a dict pinch information to it:
package require Tk grid [ttk::label .mywidget -text "My Widget"] # Define the binding to grip a manually "namespaced" arena and the payload bind .mywidget <<UI:Request:Submit>> { .mywidget configure -text \ "ID: [dict get %d id], User: [dict get %d user]" } # Generate an arena pinch a dictionary information payload event make .mywidget <<UI:Request:Submit>> -data [dict create id 42 personification "Jake"]Or amended yet — build a reusable people pinch methods to "subscribe" widgets to events, and past iterate complete a database of subscribers and emit events pinch information (or a "payload") if needed:
# Publish a generic arena pinch a payload to each subscribed widgets # Imagine this arsenic immoderate 'EventManager' class method people {eventName {payload ""}} { if {![dict exists $subscribers $eventName]} return foreach w [dict get $subscribers $eventName] { if {[winfo exists $w]} { arena make $w $eventName -data $payload } } } # Subscribe a widget to perceive for a circumstantial virtual event method subscribe {w eventName} { # Ensure the arena introduction exists to debar "key not found" errors if {![dict exists $subscribers $eventName]} { dict group subscribers $eventName {} } # 'ni' (not in) cheque prevents the aforesaid widget from being added twice # This ensures a azygous 'publish' doesn't trigger the aforesaid widget aggregate times if {$w ni [dict get $subscribers $eventName]} { dict lappend subscribers $eventName $w } }And connected the a subscriber's side, whether it's a TclOO "object" (using Tcl 9 callbacks) aliases a elemental procedure:
# Object-oriented TclOO way # Imagine this arsenic a method successful immoderate class method onUpdate {data} { puts "Method received: [dict get $data user]" } # Subscribe: successful the constructor, pass arena head 'evt_mgr' that .mywidget wants to subscribe [my evt_mgr] subscribe .mywidget <<UI:Request:Submit>> # Bind: Connect the virtual arena to the callback # Pass %d truthful the event's -data reaches the method's 'data' argument bind .mywidget <<UI:Request:Submit>> [callback onUpdate %d] # === OR === # Via a modular process (with world scope pollution, ew!) proc myGlobalHandler {data} { puts "Proc received: [dict get $data user]" } # Subscribe and bind $evt_mgr subscribe .mywidget <<UI:Request:Submit>> bind .mywidget <<UI:Request:Submit>> {myGlobalHandler %d} # Publish the arena pinch a information payload to each subscribers evt_mgr people <<UI:Request:Submit>> [dict create id 42 personification Jake]"This measurement your widget(s) tin subscribe to a virtual arena <<UI:Request:Submit>>, which your Event Manager would past people pinch aliases without a "payload" attached.
Think of this for illustration C# Events and Delegates, aliases CustomEvent dispatch successful JavaScript, aliases PyPubSub and blinker signals successful Python.
By emitting your ain <<Virtual>> events and subscribing (or "binding") UI items and logic to those, you tin build very analyzable GUIs without having to woody pinch difficult coupling, whilst having each view elements independently respond to thing happening to data successful your program.
In the pursuing objection the ValidationResult event's payload is sent to subscribers arsenic a dict. Here it contains conscionable 1 key-value brace {isValid 1}, but arsenic you've seen, it tin clasp galore much types of validation booleans aliases moreover free-form matter messages depending connected your goals. Here virtual events are utilized to group up connection betwixt different widgets and windows, allowing immoderate number of them to perceive to events: popup windows, validators, the inspector window, the "Send" button. Or emit them pinch cardinal presses and fastener clicks:

Virtual events are a portion of the Tk package and hence unavailable successful "pure Tcl", because they request to hindrance to circumstantial widgets. But this doesn't mean you can't widen your your EventManager people to make it spell done a dict of subscribers and telephone the provided callbacks arsenic commands, and not arsenic Tk's virtual events.
With specified an arena guidance class, you tin do immoderate cool things. How astir a CLI instrumentality pinch a pseudo-graphical interface driven by a "game loop"? Here a tick arena is triggered each 50ms, and the due commands are subscribed to it to tie their animated graphics into the console independently. The tick_count adaptable acts arsenic a world timebase, allowing subscribers to cipher their animation shape by comparing the existent tick to their soul "start" tick. Screen transitions are driven by an arena that captures and broadcasts the input string.

All of this non-blocking goodness is moving in a azygous thread. And we haven't moreover covered Tcl's threading capabilities yet! How galore smoothly animated and responsive console apps person you seen arsenic of late? Tcl excels successful this respect owed to its "event-first" philosophy.
By making systems pass via events, you tin create types of components, which would beryllium very difficult to do pinch an imperative, tightly-coupled approach.
In the pursuing example, the scrollbar widget is wired to enactment arsenic a driver, aliases a "slider". It doesn't attraction who listens, and simply broadcasts really acold its grip has traveled. Canvases person nary hint that the scrollbar exists astatine all, and are simply listening a definite event, expecting a information "payload" pinch a 0-1 fraction multiplier. They return that worth and independently cipher really acold each 1 needs to travel. We could create 10 windows pinch 10 much canvases of different height, and they would auto-subscribe to a shared arena and scroll together, aliases await immoderate "private" events.
At immoderate constituent we tin destroy immoderate of these UI components, and the programme will proceed to tally without exceptions aliases "missing references", because location are none. There are only subscribers and events.

Such data-oriented, event-driven creation lands itself perfectly into areas wherever high-performance, reliable, decoupled, and robust dual-sided synchronization of authorities is required. Particularly erstwhile building systems pinch intelligibly defined MVC-like roles. For instance, this is precisely why the Unity crippled motor squad is truthful hell-bent connected promoting their Data-Oriented Technology Stack, for it's the only type of architecture that tin realistically beryllium scaled almost infinitely, while allowing developers to transparently negociate ample systems.
Tcl/Tk is simply a cleanable fresh for this since its very first release!
This attack is ace effective and intuitive simply because Nature itself has already fundamentally "solved" systems communication, and the event-driven architecture is very adjacent to really the existent world "works". Like erstwhile humans and animals respond to events and create caller events, based connected the accusation they person and the skills, knowledge, resources and goals they have.

In fact, ✈ PIOSEE, a elemental and effective decision-making model I covered connected the blog, is fundamentally an "event-driven" decision pattern:
- An arena happens which you whitethorn take to react, aliases "subscribe" to ("bind")
- You stitchery accusation related to the event, aliases that the arena carried straight ("data")
- Based connected this accusation you determine connected the strategy ("callback")
- The chosen action is performed ("eval")
- The results are evaluated and early strategies adjusted arsenic needed ("try/on error")
Beautiful, is it not?
With specified an integrated attack and Tcl's stable, reliable, non-API-breaking instauration you tin create "timeless" interfaces for modern, platform-native backends, ensuring that your logic stays accelerated and your UI stays decoupled, sloppy of really the underlying OS evolves (or regresses successful lawsuit of Windows 11). Tcl/Tk codification written 20 years agone still runs today. By building your UI pinch Tcl, you'd beryllium building a instrumentality that would apt 1 time tally connected a server connected a Lunar colony, pinch small to nary codification changes needed.
Python, Tkinter and Tk
Did you cognize that Python comes bundled pinch Tcl/Tk wrapper called "Tkinter"?
Indeed, Python tin really tally Tcl code. The problem is — executing Tcl commands wrong Python scripts is awkward, but moreover worse is the truth that for a agelong clip the bundled Tk installation defaulted to the classic, "ugly" widget/font group alternatively than the Ttk Themed Tk widgets.
Thankfully, Python added Tcl/Tk 8.5 ttk widget support successful versions 2.7 and 3.1, but galore Python tutorials still usage the aged explanation and fastener alternatively of ttk.Label and ttk.Button. If you travel those aged guides and conscionable do from tkinter import * , immoderate Tk GUIs you create via Python will look for illustration they came consecutive from the 90s. You request to explicitly petition the ttk type pinch from tkinter import ttk and usage ttk widgets successful Python scripts to make your apps look "OS-native".
And yet still, moreover pinch ttk, Python doesn't automatically prime the champion taxable for the OS! It often defaults to "clam", "alt", aliases the dreaded "classic" connected Linux, which look dated. It's a messiness of a Tk bridge! To "fix" this, Python developers discarded hours trying to get PyQt aliases PySide to activity (and woody pinch licensing and bloat). They assume Tk is incapable of modern UI, simply because the provided span to it is clunky, ultimately giving some languages a bad reputation!
All of this leads to confusion, and an endless activity of complaints connected the web, like this one:

If you want the smoothest, "native" Tk experience, whenever possible, pair Tk pinch what it was primitively designed for — Tcl. In Python, the GUI is still an afterthought, while successful Tcl, the GUI is the cardinal characteristic of the language.
Then, if astatine immoderate constituent you request to telephone Python scripts from your Tcl/Tk tools, trigger those a command-line scripts pinch exec. Or amended yet — constitute your Python programs to support standard streams — stdin/stdout. By utilizing the unfastened bid pinch a tube awesome | successful Tcl, you tin spawn a Python process as a persistent child. This will fto you to telephone Python functions by sending information to it pinch puts, and receiving processed results via fileevent aliases gets. Such asynchronous span will support your Tk GUI responsive while utilizing Python’s monolithic room ecosystem for illustration NumPy aliases Pandas for dense math, skipping the headache-inducing tkinter altogether.
Here's an illustration really this could beryllium achieved.
In your Tcl "host" script:
# Open Python arsenic a *persistent pipe* for bi-directional communication # The 'r+' mode allows to publication and constitute to the process set py_bridge [open "|python3 -u your_script.py" r+] # Ensure the tube is *non-blocking* truthful the Tk GUI doesn't frost forever fconfigure $py_bridge -blocking 0 -buffering line # Create a handler to respond erstwhile Python sends information back fileevent $py_bridge readable { if {[gets $py_bridge data] >= 0} { puts "Received from Python: $data" # Update your Tk UI widgets here... } } # Use this to *send* a bid to Python puts $py_bridge "calculate_data_chunk_1"And successful your your_script.py you'd person this:
import sys # Listen for commands from the Tcl pipe for statement successful sys.stdin: cmd = line.strip() if cmd == "ping": # Send information backmost to Tcl stdout print("pong") elif cmd == "exit": break"This is akin successful principle to the "Thin-Client UI", discussed below. Sure, technically this intends that your app would incorporate 2 codebases, but you'd extremity up pinch a professional, system attack wherever you'd person a UI Layer and a Logic Layer, and a clear separation of concerns:
- Use Tcl/Tk for what it does champion — rapid, unchangeable GUI deployment, which being wide cross-platform perfectly compliments Python's cross-platform capabilities
- Utilize Python for what it does champion — data processing and a immense magnitude of disposable libraries
And if you don't request to telephone those Python libraries, you could, you know… write your logic correct there, successful your Tcl scripts, without immoderate other dependencies. Tcl is simply a very tin language, arsenic I show done galore examples below. Considering that it's cross-platform, CLI/GUI capable, interpreted, comparatively easy to study and purely BSD-licensed, you whitethorn want to take to study astatine slightest the basics of Tcl/Tk, to complement your package improvement toolkit, and debar those wonky Python<>Tcl API bridges for good.
Where to Learn Tk
As you now now, Tk is conscionable a Tcl extension. It's reasonably easy to learn, arsenic agelong arsenic you person a basal knowing of Tcl itself. Tk provides widgets and events, and you tin harvester them successful immoderate measurement you want to build almost immoderate UI your exertion needs. For the logic — "pure Tcl" will supply the intends to interact pinch channels, execute calculations, create and negociate kid processes and threads etc.
As mentioned previously, TkDocs has each the info you'll ever request to build your GUIs, and offers champion practices for building Tk interfaces: i.e. utilizing the grid layout head alternatively of pack, and addressing important quirks aliases needing to group a bequest bid option add *tearOff 0 to disallow tearing apical model menus disconnected — a relic of Motif-style X11 UI that is an alien conception to modern operating systems.
As a prize — TkDocs provides examples successful respective languages: Tcl, Ruby, Python and Perl, truthful you tin still effort and build your UIs from wrong Python, if you wish.
Tcl 9.0 UI Widget Demos
As I mentioned above, you don't request to hunt for Tk apps connected the web successful hunt for demos to study from.
Most "batteries included" Tcl/Tk installers travel pinch the modular Tcl and Tk library folders. You tin easy cheque if your Tcl instal has those, and wherever they are located. To do this, tally wish and usage the pursuing commands:
puts $tk_library puts $tcl_libraryThat tk_library directory successful astir cases contains a files afloat of awesome UI widget demos! Most importantly — you tin quickly jump to the root codification of each one, to spot really it's put together:

I opportunity "usually", because location tin beryllium differences betwixt Tcl/Tk 9.0 installations, arsenic good arsenic standalone Tclkits and Zipkits. Demos are stripped retired from Zipkits, but whitethorn beryllium recovered successful Tclkits. You could conscionable download a Tk Tclkit, tally it and entree demos pinch root [file subordinate $tk_library demos widget], but why settee pinch half-measures erstwhile you tin conscionable instal Tcl/Tk properly?
You're now astir apt confused, because we haven't discussed Tclkits and Zipkits yet. No worries.
Just travel my lead: get and deploy a "batteries included" Tcl/Tk 9.0 package. To do this, jump to the "Tcl/Tk for Windows", "Tcl/Tk for Linux" aliases "Tcl/Tk for macOS" section, and instal the due Tcl/Tk package for your OS.
Then, to entree the demos, create a platform-agnostic demos_launcher.tcl record pinch the pursuing contents:
package require Tk; group tkver "tk$tk_version"; # Identify Tcl version # Try the modular Tk room adaptable (works for modular installs/Homebrew) set locations [list [file subordinate $tk_library demos widget]] # Add the Tcl 9 ZipFS soul way (for bundled Zipkits/Starpacks) lappend locations "//zipfs:/app/tk_library/demos/widget" # For "portable" BAWT installs: spell up from 'bin' to 'lib' set base_dir [file dirname [file normalize [info nameofexecutable]]] lappend locations [file subordinate $base_dir .. lib $tkver demos widget] # macOS Homebrew circumstantial fallback (sometimes the symlink building is deep) lappend locations "/opt/homebrew/opt/tcl-tk/lib/$tkver/demos/widget" set found_path "" foreach loc $locations { if {[file exists $loc]} { group found_path $loc; break } } if {$found_path ne ""} { puts "Sourcing demos from: $found_path"; root $found_path } other { puts "Error: 'widget' missing. Checked the pursuing locations:" puts " - [join $locations "\n - "]" }Finally, tally it pinch wish aliases tclsh — the demo showcase model should popular correct up.
Thanks to these demos, I recovered retired that Tk 9.0 natively supports creating tray icons and menus, without the request to usage platform-specific libraries. See the "Common Dialogs" => "5. System tray icon and notification" demo. These demos are very useful, but they utilize only the "standard" Tcl/Tk libraries. So beryllium connected a lookout for civilization packages for illustration TkDND that you mightiness find connected the web, aliases already person disposable pinch your Tcl/Tk installation, to grow your GUIs pinch moreover much cool features.

OK, now. Having familiarized ourselves pinch the GUI toolkit, let's return a look astatine different notable Tcl features and precocious topics.
Namespaces
As pinch different programming languages, it's ever a bully thought to debar polluting the world programme scope pinch functions and variables.
Tcl was created by John Ousterhout successful 1988. For the first 9 years of its life (versions 1.0 done 7.6) everything was global: location was precisely 1 world namespace for procedures, and 1 world scope for shared variables. Because managing monolithic codebases pinch conscionable prefixes was painful, Michael McLennan created a wildly celebrated object-oriented hold called [incr Tcl] (often stylized arsenic iTcl). It was iTcl that pioneered its ain namespace system to isolate classes and methods.
The Tcl halfway squad adapted the concept, and officially rolled autochthonal namespaces (the :: syntax we usage today) into the halfway connection pinch the merchandise of Tcl 8.0 successful August 1997.
Usually, to execute isolation successful C# aliases JavaScript, you'd create fixed classes aliases namespaces and nest methods and variables wrong those. Since Tcl doesn't really support "static classes", the existent "Tcl way" to forestall sanction collisions is done namespaces. However, location is simply a cardinal favoritism betwixt Tcl namespaces and those recovered successful C-style languages:
- In C#, a namespace is simply a logical grouping for types (classes, interfaces). You don't "run" aliases "execute a namespace" (since there's thing to run), and alternatively instantiate aliases reference the types wrong it
- In Tcl, a namespace is simply a container for commands and variables. It is much akin to a JavaScript entity utilized arsenic a module, alternatively than a elemental "logical path"
Namespaces were disposable arsenic a measurement to "nest" information successful Tcl, agelong earlier object-oriented programming patterns took complete the world of programming. And, successful opposition to the C-like languages, wherever fixed classes aliases properties are created astatine the commencement of the programme and beryllium until exertion exit, in Tcl namespaces tin beryllium created and deleted astatine immoderate time, furthering their domiciled arsenic the "ancestors" to the OO shape successful Tcl.
Namespaces are created arsenic children of the guidelines namespace, known arsenic :: — and, dissimilar commands and their arguments which are separated pinch spaces and tabs, the namespaces and their contents are usually accessed pinch the aforesaid namespace separator, i.e. ::SomeNamespace::somevar. These are known arsenic "fully qualified paths".
Being "containers", namespaces successful Tcl are created pinch the eval prefix successful Tcl, while commands and variables are declared wrong the scope of a namespace, and the codification wrong the namespace assemblage executes the infinitesimal the namespace is defined:
# Tcl Namespace Pattern namespace eval Logger { # 'variable' defines a var scoped to this namespace (like a 'static' section successful C-like languages) adaptable count 0 proc log {msg} { adaptable count incr count puts "\[$count\] $msg" } # We tin take what to "export" aliases expose to the extracurricular scopes namespace export Log } # Access namespace elements via the afloat qualified path ::Logger::log "Hello Tcl" # Delete the namespace, arsenic if it were a bid aliases an 'object' namespace delete LoggerYou besides don't person to state all elements and procedures erstwhile creating a namespace, and tin adhd caller items to 1 conscionable by addressing them via a afloat qualified way to the namespace. Doing this whitethorn not beryllium the champion idea, because you tin accidentally create a "spaghetti state" by defining namespace variables and procedures successful 15 different files, but it's technically possible:
# Add a adaptable to a namespace from immoderate spot successful the script set ::Logger::timestamp [clock seconds] # Access the caller adaptable via a afloat qualified path puts $::Logger::timestampNamespace Ensembles
Now, wouldn't it beryllium cool to not person to type the ::fully::qualified::path to entree the procs and variables wrong the namespace each the time?
It wholly would, and it's perfectly possible. You tin create a "Namespace Ensemble" bid pinch the aforesaid sanction arsenic the namespace, which would enactment arsenic a "command dispatcher" to thief group immoderate functionality nether a shared name. Sort of for illustration string! Indeed, this is what each of those "default" Tcl bid groups are — list, dict, record and others, provided by various packages — these are namespace ensembles wherever commands pinch akin purposes are grouped together for clarity and to debar sanction clashes.
The cardinal favoritism is that a namespace ensemble is registered arsenic an existent command, transforming a fixed instrumentality into a dynamic, modern "interface".
So to debar having to type the ::fully::qualified::path usage namespace ensemble create:
# Tcl Namespace As Ensemble Pattern namespace eval Logger { adaptable count 0 proc log {msg} { adaptable count incr count puts "\[$count\] $msg" } namespace export log namespace ensemble create } # Access namespace elements for illustration you would pinch immoderate modular ensemble Logger log "Hello Tcl"This is starting to look much and much for illustration a "method" telephone connected an "object", doesn't it?..
Well, that's nary accident. That's why unless you are simply grouping a fewer commands into an ensemble aliases building a very basal utility, you are amended disconnected utilizing a overmuch much modern summation to the language: the object-oriented TclOO extension. This hold became an integral portion of the Tcl halfway starting pinch type 8.6 and provides a native, high-performance object-oriented model that brings Tcl's elasticity into the modern era.
But earlier we dive into TclOO, location is 1 foundational conception we must clarify.
The "Everything Is a String" Conundrum
One of the astir misunderstood aspects astir Tcl is the "Everything is simply a String" (often abbreviated arsenic "EiaS") look casually thrown around, which causes unnecessary confusion. This is simply a much method topic, but spot me: you simply must person this knowledge. Overlooking it would inevitably stifle your maturation arsenic a Tcl programmer.
Technically, this look could be:
In Tcl, the Canonical Form of Every Value is simply a String*
*The asterisk present means: while each worth tin beryllium represented arsenic a string, it isn't ever stored arsenic one. Tcl is smart capable to shop information successful axenic binary, integer, aliases database formats internally, only generating a drawstring practice erstwhile perfectly necessary. So there's really nary bully measurement to building this conception successful a "one-liner", truthful alternatively 1 must understand the underlying mechanics of Tcl to spot wherever the disorder comes from.
You access everything pinch strings, and get/set values. However, nether the hood it's much involved:
- Tcl uses a hierarchical namespace system
- Each namespace is simply a chopped entity building that consists of it's ain lookup hashtables
- Strings efficaciously enactment arsenic keys successful key-value brace hashtables that shop pointers to Tcl_* C-structs created for each of the disposable Namespaces
And present are the hashtables successful question:
- varTable | <String, Tcl_Var*> — Hash array for Variable Entities
- cmdTable | <String, Tcl_Cmd*> — Hash array for Command/Proc logic
- childTable | <String, Tcl_Namespace*> — Hash array for nested kid Namespaces
Each Tcl_Var acts arsenic a head that points to a Tcl_Obj.
So get this: Tcl_Obj entity is "dual-ported"! It holds a string representation and an internal representation:
- The String Representation: a UTF-8 type of the data, the "what it looks like" for the interpreter
- The Internal Representation: wherever the "Binary Byte Array/List/Integer" lives, the existent type and contents for generating and executing accelerated C-code. It tin only clasp 1 soul type astatine a time, and contains a national of pointers to businesslike structures (like a C-array for lists, aliases an integer/double for math)
Here's the Tcl_Obj struct meaning taken straight from the Tcl's root code (tcl.h file):
typedef struct Tcl_Obj { Tcl_Size refCount; /* When 0 the entity will beryllium freed. */ char *bytes; /* This points to the first byte of the * object's drawstring representation. The array * must beryllium followed by a null byte (i.e., at * offset length) but whitethorn besides contain * embedded null characters. The array's * retention is allocated by Tcl_Alloc. NULL means * the drawstring rep is invalid and must be * regenerated from the soul rep. Clients * should usage Tcl_GetStringFromObj or * Tcl_GetString to get a pointer to the byte * array arsenic a readonly value. */ Tcl_Size length; /* The number of bytes astatine *bytes, not * including the terminating null. */ const Tcl_ObjType *typePtr; /* Denotes the object's type. Always * corresponds to the type of the object's * soul rep. NULL indicates the entity has * nary soul rep (has nary type). */ Tcl_ObjInternalRep internalRep; /* The soul representation: */ } Tcl_Obj;This indirection is what allows the variable's sanction and its traces successful Tcl to stay changeless moreover erstwhile the underlying internal information type is swapped aliases updated based connected the type of the operation: erstwhile the information is treated arsenic a string, aliases a list, aliases a dictionary aliases arsenic a byte array — whenever needed, the soul practice of the information changed (or "shimmered") into a caller type that the C-engine tin activity with.
Ergo, the truth that values are canonically strings, doesn't mean that Tcl can't woody pinch numerals, system information aliases civilization information types. It besides doesn't mean that each information is stored arsenic strings internally.
What besides fuels the disorder astir the "everything" successful the "Everything is simply a String" rule is that it only applies to values. Whereas Tcl supports a typical adaptable type that contains pointers to different variables pinch values stored successful representation — Arrays.
Let's spot what happens if we use the aforesaid "Everything is simply a String" accuracy to an Array:
# Create a "standard" adaptable and publication it set var_a "String inside" puts $var_a; # SUCESS => "String inside" # Create an array array group var_b {fruit pome value 100} # Let's people it! puts $var_b; # ERROR! => "can't publication "var_b": adaptable is array"What?! That's not a drawstring astatine all! Have we been lied to?
Not really. An Array is not a value, you cannot walk it arsenic an statement to a bid (you tin only walk its name). So the EiaS rule doesn't moreover request to use to it. The variables that the Array points to are value-type, and are accessed pinch strings, truthful the EiaS rule applies to them arsenic expected:
# Create an array array group var_b {fruit pome value 100} # Access a drawstring worth utilizing a drawstring sanction (or "key") puts $var_b("fruit"); # SUCCESS => "apple"Now here's thing astir Tcl that blew my mind:
The Same String Can Represent Both Data and a Command
Remember really each Tcl namespace contains hashtables for commands and variables? Well, since these Tcl_Obj and Tcl_Cmd* hashtables are separate, they tin independently incorporate identical keys!
Nothing's stopping you from having a drawstring "myStr" to beryllium coming successful some the Variable hashtable AND the Command hashtable!
Check this out:
# Create a VARIABLE pinch worth 'foo' set myStr foo puts "myStr first worth is '$myStr'" # Create a PROCEDURE pinch the aforesaid name # Give 'arg' a default worth truthful the telephone doesn't neglect without immoderate args proc foo {{arg "~nothing~"}} { puts "Received '$arg' pinch the bid call" } # Call 'myStr' arsenic a *command* without arguments $myStr # Call 'myStr' arsenic a *command* pinch arguments $myStr hello # Use 'myStr' arsenic a variable, arsenic DATA is unchanged puts "myStr worth is still '$myStr'!"And here's the output:
myStr first worth is 'foo' Received '~nothing~' pinch the bid call Received 'hello' pinch the bid call myStr worth is still 'foo'!The quality is where Tcl looks for the provided "string key":
- $MyVarName — the worth of the adaptable becomes the cardinal for the Command lookup, because it's placed first successful the bid string
- puts $MyVarName — the worth becomes the cardinal for the Variable lookup, arsenic it sits aft the bid that expects an argument
You could ne'er do this pinch JavaScript, Python, C#, aliases C++ and the for illustration owed to their unified namespaces. If you specify let foo = 5, you can't past travel it up pinch function foo() {} successful the aforesaid scope without overwriting the first declaration aliases triggering an error. I'm not saying this makes Tcl someway "better". If anything, Tcl needs to beryllium system this measurement owed to its homoiconic nature, stemming from its spartan group of syntax rules.
Native Introspection
At immoderate infinitesimal you tin inspect the contents of the aforementioned hashtables:
- info vars — looks up each variables successful the existent namespace
- info commands — returns each commands, successful the existent namespace
- info procs — a subset of the supra which only returns commands created pinch the proc bid successful scripts, excluding C-implemented built-ins for illustration group aliases puts
- puts [namespace children ::] — will output each kid namespaces of a specified namespace. In this illustration — each namespaces existing nether the guidelines :: namespace
Pure Binary Data? Not a Problem With Tcl!
Here's definitive impervious that Tcl is not constricted to only ASCII strings, and you tin manipulate the soul practice of the Tcl_Obj directly. You now cognize that Tcl stores information successful Tcl_Obj structures. This successful truth allows it to clasp a earthy byte array without corrupting aliases losing data, moreover if those bytes would beryllium "illegal" characters successful a modular drawstring (like a null terminator \0).
In this illustration Tcl sounds a binary file, transforms it to hexadecimal and back, and writes it into a caller file:
# Do this successful a NON-interactive convention (as a "one-shot script")! # Open a record for binary data set fh [open "image.png" r] # CRUCIAL: Need to forestall Tcl from messing pinch the statement endings # Explicitly group some translator AND encoding to binary # -translation binary: Prevents CR/LF (\r\n) translation # This ensures the Tcl_Obj created by [read] is simply a axenic ByteArray fconfigure $fh -translation binary set rawData [read $fh] close $fh # Binary Manipulation via the 'binary' ensemble # Use 'binary encode hex' to toggle shape the information to a hex string set hexData [binary encode hex $rawData] # Then decode it backmost into binary set restoredData [binary decode hex $hexData] # Verification of the first 8 bytes (PNG Signature) from the hex string puts "PNG Hex Signature (Transformed): [string scope $hexData 0 15]" # => RESULT: 'PNG Hex Signature: 89504e470d0a1a0a' (which is valid for PNGs) # Open the caller record and constitute the resuolting byte array information into it set retired [open "same_image.png" w] fconfigure $out -translation binary puts -nonewline $out $restoredData close $out # => RESULT: the aforesaid PNG image, without a azygous byte changed!There's a full binary ensemble, full of commands for businesslike binary information manipulation, truthful you aren't forced to usage Base64 encode/decode to move your bytes astir arsenic UTF Strings.
Avoid commands for illustration subst aliases regexp to run connected binary data, for they unit drawstring interpretation. If you telephone these connected a binary Byte Array, Tcl might person to make a String Rep to execute the task (depending connected Tcl type and encoding) and person the soul binary information into a UTF-8 drawstring rep, discarding the binary representation.
As agelong arsenic you ne'er dainty binary information for illustration a modular string, you will not corrupt it. To grip binary data, Tcl must support the Internal Rep arsenic a bytearray and the String Rep uninitialized and untouched to debar representation bloat. So conscionable do fconfigure $channel -translation binary erstwhile dealing pinch thing that's not a text.
Can you feel the power? Are you now seeing really Tcl is measurement much than conscionable a "string processor"?

As for capacity considerations, though Tcl is simply a bid language, it doesn't conscionable re-parse strings each time. Once a adaptable is utilized arsenic a statement, Tcl's motor caches the compiled bytecode wrong the information entity itself, truthful it doesn't person to re-parse the characters each clip it hits that loop.
But erstwhile it does request to re-parse the information — it's called…
Shimmering
As a consequence of Tcl being a dual-ported language, and the truth that the underlying C-engine of the expert still needs to cognize the type of information it's dealing with, arsenic a high-level interpreted connection Tcl supports type conversion. In Tcl world this is called "shimmering". The funny sanction is owed to the truth that depending connected the type of the cognition being performed, opportunity — drawstring trim aliases lreverse — the expert has to wholly re-generate the soul practice of data, arsenic the erstwhile cognition happens connected information arsenic a string, and the second must spot it arsenic a list. So the underlying information type is perpetually flickering aliases "vibrating" betwixt different states to fulfill the commands you are running.
Thankfully, shimmering only occurs erstwhile the expected information type doesn't lucifer what's already stored successful the soul practice of the Tcl_Obj. To support your codification accelerated and information safe, dainty information for illustration a specialized entity of the expected type. If it starts arsenic a dict, support utilizing dict commands. If it’s binary, instrumentality to the binary ensemble. The infinitesimal you usage a "generic" drawstring bid connected immoderate non-string information type, you’ve initiated a shimmer, slowing down the programme and perchance changing the data.
You tin observe shimmering successful action by monitoring the soul practice pointers. This allows you to diagnose whether unnecessary information transformations are occurring successful "hot paths" aliases compute-heavy sections of your code. To peek nether the hood, Tcl provides a "hidden" diagnostic command: tcl::unsupported::representation. Here is really you usage it:
puts "=== Create a database and append to it. Fast and nary shimmering" set listA [list a b c] puts [tcl::unsupported::representation $listA]; puts "DATA: $listA" set listA [lappend listA "d" "e"] puts [tcl::unsupported::representation $listA]; puts "DATA: $listA" puts "=== Sorting a database recreates it, causing soul rep to update wholly ===" set listA [lsort $listA] puts [tcl::unsupported::representation $listA]; puts "DATA: $listA" puts "=== Calling 'string' causes shimmering AND generates a drawstring rep! ===" set listA [string trim $listA] puts [tcl::unsupported::representation $listA]; puts "DATA: $listA"Output:
=== Create a database and append to it. Fast and nary shimmering value is simply a database pinch a refcount of 2, entity pointer astatine 0x16ecf4a77f0, soul practice 0x16ecf47a7f0:0x0, nary drawstring representation DATA: a b c value is simply a database pinch a refcount of 2, entity pointer astatine 0x16ecf4a77f0, soul practice 0x16ecf47a7f0:0x0, nary drawstring representation DATA: a b c d e === Sorting a database recreates it, causing the soul rep to update wholly === value is simply a database pinch a refcount of 2, entity pointer astatine 0x16ecf4a7a30, soul practice 0x16ecf47a470:0x0, nary drawstring representation DATA: a b c d e === Calling 'string' causes shimmering AND generates a drawstring rep! === value is simply a axenic drawstring pinch a refcount of 2, entity pointer astatine 0x16ecf4a70d0, drawstring practice "a b c d e" DATA: a b c d eHence the Golden Rule of Tcl Performance: an entity is astatine its fastest erstwhile it has a valid Internal Representation and No String Representation. Ideally, only strings should person a drawstring representation. Data that's not strings should not beryllium operated pinch immoderate drawstring commands, including being interpreted arsenic a drawstring simply by reading it arsenic one, pinch puts for instance:
puts "=== Create a list. No drawstring rep" set listA [list a b c] puts [tcl::unsupported::representation $listA]; puts "DATA: $listA" puts "=== 'Peeking' astatine information utilizing 'puts' generates and stores a useless drawstring rep! ===" puts "Just logging the contents of $listA..." puts [tcl::unsupported::representation $listA]; puts "DATA: $listA"Here's the result: Passing any adaptable to a bid that expects a drawstring forces Tcl to make a drawstring representation. leading to representation bloat, unless it's already a drawstring — announcement beneath really no string representation turns into drawstring practice "a b c":
=== Create a list. No drawstring rep value is simply a database pinch a refcount of 3, entity pointer astatine 0x1fabd84c8a0, soul practice 0x1fabf385700:0x0, nary drawstring representation DATA: a b c === 'Peeking' astatine information utilizing 'puts' generates and stores a useless drawstring rep! === Just logging the contents of a b c... value is simply a database pinch a refcount of 3, entity pointer astatine 0x1fabd84c8a0, soul practice 0x1fabf385700:0x0, drawstring practice "a b c" DATA: a b cSuch "Lazy String Generation" perfectly illustrates the "EiaS" principle, arsenic the canonical shape of each worth is so a string, and if 1 is requested, it will beryllium generated connected the fly. So this is not a bug, this really is an architectural decision, aimed astatine performance. By caching the drawstring erstwhile it is generated, Tcl avoids the overhead of re-calculating that practice connected each consequent call.
Developers pinch extended acquisition pinch C-like languages are astir apt worried aft reference this. But fearfulness not, this short summary should group things straight:
- As agelong arsenic you dainty your adaptable arsenic having a "static" type, Tcl is arsenic accelerated arsenic immoderate different purely interpreted language. If a adaptable is simply a list, usage lappend, lindex, lrange etc, and debar utilizing drawstring aliases regexp connected it. This prevents the overhead of reallocating representation and ensures Tcl operates connected the existing soul practice of the information directly
- For ample datasets that require modification — walk the name of the adaptable to procedures utilizing upvar alternatively than passing the worth itself. This creates a section othername pointing to the adaptable successful the caller's scope. Much for illustration the ref keyword successful C# this allows the process to run straight connected the original storage. This avoids the monolithic overhead of copying the full information building into the procedure’s section scope — thing that JavaScript handles automatically for objects, but fails to do for primitive values, for instance
- Finally, likewise to JavaScript, Python and the like, Tcl gives you the state to… not attraction what the adaptable is, and alternatively conscionable attraction connected what you do to it. It will shimmer it into a caller information type for you nether the hood. Tcl is simply a "glue" connection aft all, its extremity is to beryllium arsenic hassle-free arsenic imaginable to fto you get things done
BTW, Tcl provides plentifulness of hidden optimizations. For example, if you state 1 adaptable and group the worth of different another 1 to it:
set listA [list a b c] ;# Refcount 1 set listB $listA ;# Refcount 2 (Both variables constituent to the aforesaid Tcl_Obj!)…Tcl besides doesn't create a transcript correct away. It points listB to the aforesaid Tcl_Obj arsenic listA. And since Tcl has its ain reference counting strategy successful place, if you later effort to alteration adaptable listA, Tcl will recognize that location are 2 variables pointing to the aforesaid reside space. And if were to modify the representation successful place, listB would besides change, which would break Tcl's value-semantics. So only successful this lawsuit does it clone the information object, modifies the clone, and points listA to the caller one. Yep, it's the bully aged Copy-on-Write (CoW) pattern.
Tracing Shimmering With the "Trace" Command
One of the signature features of Tcl is its expertise to trace things. You can, for instance, spot a trace connected a adaptable aliases a command, to trigger a book erstwhile a definite arena occurs with/to it: writing, reading, execution etc. It's commonly utilized to execute clean-up erstwhile a definite adaptable goes retired of scope (like a record reference process reaching its end). Or erstwhile you want a definite artifact of codification to tally each clip matter changes wrong an introduction field. Or immoderate UI authorities adaptable changes, which mightiness telephone a bid of commands successful return etc.
# The callback procedure # 'name1' is the adaptable name, 'name2' is the scale (if it's an array), 'op' is the operation proc logChange {name1 name2 op} { # upvar lets america look astatine the adaptable successful the caller's scope upvar 1 $name1 var puts "TRACED: Variable '$name1' modified pinch cognition '$op'. New worth length: [string magnitude $var]" } # Define a adaptable and group up the trace for 'w' (write) operations set myData "Initial binary-like data" trace adhd adaptable myData constitute logChange # Now, immoderate modification triggers the proc set myData "Updated data" # => RESULT: TRACED: Variable 'myData' modified pinch cognition 'write'. New worth length: 12 # To extremity tracing: trace region adaptable myData constitute logChangeBy wrapping this bid into a one-file shimmer module, we tin create a civilization diagnostic instrumentality to hunt down shimmering successful real-time. It would fundamentally enactment arsenic a runtime type-checker for your data-critical paths, ensuring your variables enactment successful their optimized soul representations during development.
💾 Download the module here. It's documented in-code and contains usage examples.
How to usage "Tcl modules": if you don't yet person a dedicated directory for each of your Tcl modules — make one. Or conscionable trial by creating a sub-directory adjacent to your book and placing this .tm record into it. Source it for illustration so:
# Generate the way to the 'modules' subdir set baseDir [file dirname [file normalize [info script]]] set devLib [file normalize [file subordinate $baseDir "modules"]] if {[file isdirectory $devLib]} { tcl::tm::path adhd $devLib } # Source successful the civilization module package require shimmerThen usage the package to instrumentality your variables.

Consider this an elaborate objection connected really the trace bid could beryllium utilized to widen Tcl's "self-diagnostic" abilities, and arsenic an assistance to usage during improvement of ample devices wherever information integrity is paramount. Otherwise, conscionable clasp the elasticity of "EiaS". Tcl is simply a dynamically-typed connection aft all.
The Power of Tcl Dictionaries
You mightiness presume that because Tcl uses strings for its interface, it lacks JavaScript’s easiness of usage erstwhile moving pinch nested data. To spot the challenge, look astatine really nesting is handled successful JavaScript:
conf.ui = {}; conf.ui.themes = {}; conf["ui"]["framework"] = "JQuery 3.7.1"; conf["ui"]["themes"]["selected"] = "System"; console.log(conf["ui"]["themes"]["selected"]);
Except… In JS everything is an object, and each level of a multi-level key-value hashtable links to different object. This intends you request to explicitly initialize each level of your nested hashtable. So if conf["ui"] aliases conf["ui"]["themes"] don't beryllium by the clip you effort to group a worth for conf["ui"]["themes"]["selected"], JS will propulsion a TypeError.

Here's the same, done successful Tcl:
dict group conf ui model "JQuery 3.7.1" dict group conf ui themes selected "System" puts [dict get $conf ui themes selected]Oh. So it can do this. And you tin moreover omit quotes for single-word arguments.
Also statement really we skipped each that entity initialization boilerplate. That's because Tcl's hashtable, aliases dict performs auto-vivification pinch the dict group command. If conf is empty, Tcl creates each of the nested "levels" for you instantly.
"But," you mightiness ask, "how is this imaginable successful a string-based language?"
The cool point is that dissimilar JavaScript, Tcl doesn't hide thing from you. You could peep into the drawstring practice of immoderate adaptable to spot really thing for illustration a multi-level dictionary tin beryllium expressed as text, by simply calling puts $conf.
You'll spot this:
ui {framework {JQuery 3.7.1} themes {selected System}}Nested braces!
- If your cardinal aliases worth has a space, Tcl wraps it successful curly braces {}
- If your worth is simply a nested dictionary, it's wrapped successful braces arsenic well
Tcl considers immoderate drawstring pinch an moreover number of elements a valid dictionary, truthful it doesn't matter really heavy the nesting goes. Each "value" successful a brace is simply different drawstring that tin beryllium interpreted arsenic a dict by the adjacent dict command.
Thankfully, Tcl doesn't unit you to manually sift done this nested data. There's a full dict ensemble of commands for you to play with:

As for lists, respective commands starting pinch "l" for illustration lindex, lset, lassign supply support for accessing nested elements wrong lists. Lists are mostly bully for matrices/grids, ordered collections aliases bulk processing of data. However, arsenic you tin imagine, moving pinch them tin get rather analyzable arsenic soon arsenic you present nested data, sloppy of the connection used. So successful galore cases you're amended disconnected pinch a dictionary, wherever an constituent tin contain a database that you tin entree utilizing a dict get command, and past construe the value of that dictionary constituent arsenic a list.
You tin execute analyzable operations by having Tcl dainty your information arsenic a nested key-value dictionary. And it’s fast! As agelong arsenic you instrumentality to only dict commands and debar generating a drawstring representation, Tcl operates straight connected the soul dictionary practice wrong the Tcl_Obj struct, avoiding the double overhead of re-parsing and storing (!) the drawstring rep.
Tcl besides offers high-level commands that fto you modify nested values without performing a tedious, C-style manual level walk, i.e. dict with:
dict pinch conf ui themes { # Inside these braces, 'selected' is now a *local* variable group selected "Dark" group last_changed [clock seconds] } # As the artifact ends, the 'conf' dict is now automatically updated!Since dict pinch unpacks each keys consecutive into the scope wherever it's called, you whitethorn opt for much "precise" dict update command, which maps definite dictionary keys to section variables:
# Only representation 'selected' to a section adaptable named 'current_theme' dict update conf ui themes selected current_theme { puts "Changing from $current_theme to Light..." group current_theme "Light" } # Only 'selected' was updated backmost into 'conf'. Fast, safe and explicit.Ideally you'd want to do this wrong an isolated scope — for illustration a process — to wholly debar adaptable sanction conflicts.
And, if you ever find yourself struggling pinch a database afloat of duplicate key-value pairs — simply shimmer it into a dict! This will automatically select retired each copy keys, pursuing the "Last Writer Wins" rule:
set duped [list consequence pome consequence cherry colour yellowish consequence banana marque "Momo Banano"] # Use the description usability to process the worth arsenic a list set deduped [dict create {*}$duped] puts $deduped # RETURNS => consequence banana colour yellowish marque {Momo Banano}As mentioned before, Tcl implements a Copy On Write (CoW) mechanism, truthful you tin walk a dictionary to a bid without the programme requiring to make a copy transcript of the data, until it's really changed. This is simply a cardinal truth to study by heart: dissimilar JS aliases Python, wherever you walk references to variables, successful Tcl you walk values. Therefore if you alteration immoderate incoming information wrong a Tcl procedure, it will dice pinch that procedure, unless you deliberately export it to the extracurricular scope. This puts different nickel into the "honesty jar" of Tcl — it will only activity pinch the data you gave it, but does support reference counting and provides optimizations to debar unnecessary representation bloat.
Of course, if you request to activity pinch information from outer scopes straight — Tcl has you covered. By utilizing upvar arsenic a ref aliases a "pointer", you tin representation an outer adaptable to a section alias. This allows you to mutate the original worth straight while ensuring that commands still person precisely what they expect: a value. Always a value.
Tcl dictionaries are very useful! I could spell connected and on, but you get the idea. Tcl is perfectly tin of dealing pinch nested dictionaries (and lists), truthful you tin cleanly shape information successful your devices without having to edifice to specialized retention engines for illustration SQLite.
Tcl Arrays
When you activity pinch strings, you're free to interpret them nevertheless you like. Hence the shimmering, and its outcomes:
- Want to run connected information arsenic if it were a database — usage the database ensemble of commands connected it
- Does this information incorporate an even number of space-delimited strings? — usage dict commands connected it. Except support successful mind, that as soon arsenic you show Tcl to dainty a drawstring arsenic a dictionary, it will automatically region copy "keys" upon shimmering the soul information practice into the dict type. Is this thing you wanted? Well, it's what you get, because dictionaries, by definition, can't person copy keys
- Then, should you construe this information arsenic a elemental database of items again, and remove 1 of the items, you'll move it into a database of an odd number of items. It's nary longer a valid dictionary
This hypothetical script shows conscionable really malleable Tcl is erstwhile it comes to information processing. But pinch awesome powerfulness comes awesome responsibility:
- If you needed to person a group of key-value pairs that is ever valid, you'd person to retrieve to ne'er shimmer it into immoderate different type, to debar unexpected information loss
- If you ever wanted to hindrance a definite "dictionary key" to a UI widget arsenic a "variable" — you can't do that, because the "key" mightiness beryllium only astatine the infinitesimal of interpreting a random drawstring arsenic a dictionary. It mightiness also… not exist. Hence the inability to hindrance thing to specified "virtual" dict keys
- And, if you ever needed to utilize Tcl's powerful trace connected a peculiar cardinal of a collection, you'd request to guarantee that such a cardinal exists arsenic an existent pointer successful memory, and not an ephemeral "maybe there's a cardinal X successful this dictionary" joke
To lick this conundrum and get a definite "type-safety" successful a non-strictly typed Tcl, you tin utilize an Array, which has typical behaviors, because:
Tcl Array is simply a level hashtable of references to variables.

This intends that its keys are unsocial strings, conscionable for illustration successful a dict, but they constituent to abstracted variables successful memory, alternatively of interpreting 1 agelong drawstring for illustration a dict would.
Such a adaptable type tin beryllium created and utilized for illustration this:
# Setting individual elements array group personification {}; # Optional, but instantly 'locks' the type set user(name) "Jason Bourne" set user(age) 31 # Accessing an element puts $user(name); # Outputs: Jason Bourne # Initialize aggregate elements astatine once array group colors { reddish #FF0000 greenish #00FF00 bluish #0000FF } # Accessing an element puts "The hex for reddish is $colors(red)"Surprisingly, this is 1 of the fewer places wherever Tcl is really arsenic strict as pure C, and moreover stricter than C#, JavaScript aliases Python, wherever you tin overwrite an array adaptable pinch a caller entity astatine will.
Let's spot what happens if we effort to shimmer a Tcl array into a dict:
set user(name) "Jason Bourne" set user(age) 31 set personification [dict create "David Webb" 31] # ERROR: can't group "user": adaptable is arrayThe correction "can't group "user": adaptable is array" efficaciously means: "You person a hashtable key, linked to an array building here. Tcl won't fto you overwrite it pinch a elemental string, because it would lead to a representation leak." So the adaptable cannot beryllium overwritten by a scalar for illustration group myArray "foo", you request to explicitly unset it first.
This is why you you can't "pass" an array to a process successful Tcl. In C#, erstwhile you walk an array, you are copying the pointer (the "reference variable"). In Tcl, location is nary pointer to copy. There is only the drawstring name, while each bid expects a value.
Therefore, to entree an array successful a process you request to usage the upvar command:
# This process doesn't 'take an array', it takes a *string name* of an array proc update_session {session_array_name status_code} { # 'upvar' links the sanction successful the caller's scope (1 level up) # to a section adaptable sanction 'local_session' upvar 1 $session_array_name local_session # Now, immoderate alteration to 'local_session' happens to the original array group local_session(last_seen) [clock seconds] group local_session(status) $status_code } # Global Scope # Initialize the array set my_session(id) "USR_99" set my_session(status) "idle" # Pass the NAME of the array (as a string) update_session "my_session" "active" # Proof of mutation: puts "New status: $my_session(status)" ;# Outputs: activeOnce a adaptable sanction is "linked" to an array (via upvar aliases array set), it becomes locked to the Array type for the life of that variable, aliases until it's unset. If you spell pinch an array, the Tcl expert itself will forestall you from accidentally shimmering an array into a string.
Because of this, pinch arrays you get a free "type lock" and a convenient measurement to group and get values of a postulation pinch $myArray(element) alternatively of [dict get $myDict element].
Still, some an array and a dict person their due usage cases:
- Use Dictionaries for data. Use these to shop key-value pairs of customer data, aliases immoderate organized information structure, including nested dictionaries
- Use Arrays arsenic a "mini-namespace" for states. For example, you tin person a ui_variables() array, and hindrance its keys to respective widgets, without having to deliberately create a abstracted namespace pinch level variables. But what's moreover much important, and what makes arrays applicable to this day successful Tcl, is the truth that arrays don't support copy-on-write. Therefore, to entree array elements extracurricular of the scope wherever the array was created, you must upvar to that scope, to scope the array, and representation a section adaptable to activity pinch its contents, preventing you from accidentally changing a copy alternatively of the root data
- Note: if you create an array adaptable inside a TclOO class, you don't moreover request to fuss with upvar at all! When TclOO creates an "instance" of a class, it generates a dedicated namespace for it. So erstwhile you past telephone $my_Array($key), the TclOO resolver will spot the name, cheque the object's namespace, find the array, and retrieve the value
"Boo! Namespaces Are Better!"
As it turns out, location is simply a long-standing statement successful the Tcl community: Arrays vs. Namespaces.
In galore modern Tcl architectures, developers for illustration to nest level variables wrong a namespace to utilizing an array for the pursuing reasons:
- "It's conscionable a namespace"
- Flat variables are conscionable existent namespaced variables
- Flat variables tin beryllium traced and bound to UI widgets, conscionable for illustration array elements
- If you want to walk a bunch of variables to a proc for illustration you would pinch an array, you tin walk the sanction of the namespace instead. And past you'd build a qualified way to the adaptable successful the procedure
- You nary longer request to usage upvar and tin conscionable activity pinch variables, which are still a azygous root of truth, because if you constitute to them — you alteration them, unless you deliberately make a transcript and mutate it
So should you ditch "obsolete" arrays and spell pinch namespaces? Not really. There are a fewer "low-level" and "ergonomic" reasons why arrays still triumph successful circumstantial scenarios:
- Indexing. Here's the quality betwixt passing a sanction of a namespace into a proc myproc {data_source, dynamic_key} alternatively of an array reference:
- Array — group data_source($dynamic_key) "value" — This is native, fast, and easy to read. Just make judge to upvar to the data_source beforehand
- Namespace — group ${data_source}::${dynamic_key} "value" — This requires double substitution and looks "hacky", whilst besides being harder for the expert to optimize (and a symptom to read!). Note to the knowledgeable Tcl devs: no, shortening this pinch aliases everyplace is not a awesome thought either
- Arrays travel pinch functions that namespaces don't have:
- array names — Instantly get a database of keys utilizing glob patterns: array names ui "button_*". To do this successful a namespace, you person to usage info vars ${ns}::button_* and past manually portion the namespace prefix disconnected the results. Every time
- array get and array group — you tin use from existent serialization. Dump an full array into a list, walk it around, shimmer into a dict, and reconstruct it instantly. Conversely, doing a "bulk dump" of a namespace requires looping done info vars and manually building a list. Every time
- Creation and representation cleanup:
- Arrays — you tin create arrays wrong procs for illustration normal variables. When a process ends, a local array is wiped from representation instantly
- Namespaces are permanent. If you create a namespace for a short-lived UI dialog, you must manually telephone namespace delete. If you forget, you person a representation leak
- Finally, pinch arrays you tin person much granular traces — you tin group a trace on the full array. It will occurrence whenever immoderate cardinal is created, deleted, aliases read, whereas successful a namespace, you tin only trace variables that already exist
It's understandable why immoderate devs see namespaces a "cleaner" attack for high-level exertion authorities (like a "UserSession" aliases "AppSettings"), arsenic utilizing a namespace pinch level variables is simply a very "C-like" measurement of thinking. It is fundamentally a "Singleton object".
However, don't make a correction of dismissing arrays entirely:
- Use Namespaces for "singletons" and world states pinch fixed keys
- Use Arrays for collections of states, wherever keys are move aliases grouped
Otherwise, if you effort to cosplay arsenic a "purist" and effort to usage a namespace to shop a move postulation of 50 UI elements, you will discarded valuable clip penning string-concatenation wrappers for group and get, efficaciously fighting the language's built-in postulation instrumentality conscionable to debar an upvar to "look cooler". Nobody cares.
Global Arrays and Namespace Upvar
Finally, here's an illustration of a shape to activity pinch "global authorities arrays" from anyplace successful your code.
Use namespace upvar to show the expert to ever look for a adaptable stored successful a peculiar namespace, and build the afloat qualified way to the array from there, for illustration so:
# Create a namespace to enactment arsenic a 'singleton' aliases an 'isolated container' namespace eval AppConf { # Initialize the array wrong the namespace adaptable Settings array group Settings { taxable "dark" type "9.0.3" } } # A process located *anywhere* (another namespace aliases global) proc apply_theme {ns arr} { # Check if the namespace exists if {![namespace exists $ns]} { correction "Configuration error: Namespace $ns does not exist." } # The 'namespace upvar' approach: # Link the global/absolute way '::AppConf::Settings' to a section short grip 'cfg' # 'namespace upvar' will build a afloat qualified way to the array namespace upvar $ns $arr cfg # Ensure 'cfg' is really an array earlier accessing keys if {![array exists cfg]} { correction "Configuration error: '$arr' is not an array successful $ns." } # Now we tin usage array commands connected 'cfg' if {$cfg(theme) eq "dark"} { puts "Applying ocular styles for type $cfg(version)..." group cfg(last_applied) [clock format [clock seconds]] } } # Execute the proc and walk the absolute way to the namespace and the array name apply_theme "::AppConf" "Settings" # Verify the change puts "Theme was applied at: $AppConf::Settings(last_applied)"This attack follows the dependency injection pattern, gives you the benefits of a namespace isolation pinch the convenience and a group of commands provided by the array ensemble.
This it's thing Tcl devs person relied connected for decades. But, nowadays, erstwhile 1 mentions "singletons" and "data isolation/encapsulation", the earthy and commonly-used shape for these would beryllium called…
Object-Oriented Programming (OOP) With TclOO

Although Tcl began arsenic a purely procedural, string-based language, modern Tcl (8.6+) includes TclOO, a high-performance, elastic entity strategy built straight into the halfway of the language.
And the measurement the OO shape was implemented successful Tcl is very elegant.
What is the bare minimum needed to make OOP possible? Encapsulation, Inheritance and Polymorphism. In C-like languages, classes are a "compile-time illusion." The CPU has nary thought what "objects" are, it only useful pinch representation addresses and jump instructions. To span this gap, C++ and C# usage 2 superior mechanisms: Memory Layout and the Virtual Method Table (vtable). On the different hand, Tcl has nary vtables, nary fixed representation offsets, and nary pointers. Shocking, I know.
What it did person since its first merchandise is the following:
- Recall that Tcl has namespaces. Those are containers for data, and they do, successful fact, encapsulate information and procedures wrong their scope, truthful this tin beryllium useful. In a way, procedures declared wrong namespaces tin moreover beryllium seen arsenic "methods" successful OOP terms
- Tcl commands tin besides beryllium made to enactment arsenic "Command Dispatchers", akin to the aforementioned namespace ensembles, truthful that you could mimic C-like Object.Method(argument) pinch Tcl's whitespaced syntax object method argument.
- Finally, since Tcl is highly malleable, polymorphism is nary rumor astatine all: you tin create, destruct and freely redefine commands, namespaces and variables, widen them pinch caller procedures and variables etc.
For a agelong time, each of the parts to instrumentality an OOP-like shape were already there. All that was missing was a robust abstraction to necktie it each together. You tin ideate that since successful Tcl immoderate first connection of a series is simply a command pinch logic, it tin beryllium treated arsenic a civilization "object" of sorts. Well, the creators of Tcl realized this arsenic well, and aft a agelong history of various 3rd-party implementations, the official, built-in TclOO people extensions became disposable successful Tcl 8.6 released successful December 2012. These gave america oo::class and oo::object commands, efficaciously providing a measurement to activity pinch Tcl "OOP-style". However, the "trick" of emulating OOP by utilizing command-routing strings has been imaginable since the conception of Tcl 1.0 in 1988!
Here's really they it was done:
TclOO is fundamentally a high-performance "bookkeeping" strategy built connected apical of Tcl's existing namespace architecture. Instead of changing Tcl's basal rules (and breaking backwards compatibility pinch decades-worth of existing code), the Tcl instauration implemented oo::object successful the shape of a blase Command Dispatcher (or Command Router).
When you specify a people pinch oo::class create MyClass and telephone [MyClass new], TclOO does the following:
- Generates a unsocial drawstring name, for illustration ::oo::Obj42. Tcl can't person "anonymous objects". Every "object" must person a sanction because it is fundamentally a command
- Creates a namespace and a bid inside. The caller bid is the introduction constituent to the "object", and the namespace provides a instrumentality for the variables and the methods
- Hooks it into a Method Map. Instead of a array of pointers, Tcl stores a database of its method names and their corresponding implementation bodies (which are fundamentally Tcl procs)
And location you person it. A measurement to instrumentality OOP-like shape successful a connection that primitively prioritized procedural simplicity. What's besides cool astir TclOO is that it does really harvester the champion of some worlds successful Tcl:
- To usage variables wrong procedures declared wrong the autochthonal Tcl namespaces, you request to "pull" the variables into procs manually pinch the keyword variable. But since TclOO "objects" shop their information and procedures successful a backstage namespace, this boilerplate has been abstracted away: erstwhile a adaptable is declared successful the class, it is automatically resolved wrong the scope of its methods. You get the isolation of a namespace pinch the ergonomics of a C# class. You tin ever inspect the sanction of the namespace the "object" exists successful pinch info object namespace $my_obj, which is besides a preferred measurement to do so, since astatine immoderate clip the bid (or the "object") tin beryllium renamed manually, which doesn't automatically rename the namespace
- Working pinch arrays becomes effortless: they are defined wrong the namespace the "object" exists in, and the methods are executed wrong the discourse of the "object’s" backstage namespace arsenic well, truthful you tin manipulate arrays straight without the changeless upvar gymnastics required successful procedural Tcl
- TclOO provides the industry-standard constructor and destructor commands, truthful you don't request to constitute you ain trace-based logic to free up resources earlier the demolition of a namespace — simply constitute a destructor and destruct an "object" pinch $my_obj destroy.
- Just for illustration successful different languages, you tin entree the methods of the people wrong itself pinch a typical call: [my methodname]. In TclOO, if you don't export a method that has a Capitalized name, it will beryllium private, and outer callers wouldn't spot it. my is the only measurement to trigger these backstage behaviors from wrong the class
- Naturally, you still person entree to Tcl's powerful devices to redefine everything, truthful you tin create guidelines classes, inherit classes from those, telephone genitor classes' methods and constructors pinch commands for illustration adjacent aliases nextto, and alteration classes astatine runtime pinch oo::define. Hell, you tin modify the "DNA" of a specific object without affecting its siblings pinch oo::objdefine!
As a result, if we rewrite the codification from the Global Arrays and Namespace Upvar conception utilizing TclOO, check retired really overmuch cleaner and much readable it becomes:
oo::class create AppConf { adaptable Settings constructor {} { array group Settings {theme "dark" type "9.0.3"} } method apply_theme {{t "dark"}} { # 'Settings' is automatically disposable here. No upvar needed. if {$Settings(theme) eq $t} { puts "Applying styles for v$Settings(version)..." group Settings(last_applied) [clock format [clock seconds]] } } # A 'getter' for 'private variables' method get_setting {key} { return $Settings($key) } } # Create an "instance" of the people and telephone a method set ac [AppConf new] $ac apply_theme # Verify the change puts "Theme [$ac get_setting theme] was applied at: [$ac get_setting last_applied]" # => Theme acheronian was applied at: Fri Apr 03 06:45:17 +0000 2026When you create immoderate "object" this way, Tcl virtually generates a caller command successful the existent namespace pinch the sanction of your "object". If you tally puts $myObj, you will spot that very ::oo::Obj42 drawstring sent backmost to you. That drawstring is the sanction of a recently registered command. This is why I kept placing immoderate mentions of "objects" and "instances" successful quotes: they picture the aforesaid point — a command, thing more.
TclOO provides respective useful helpers which make consciousness successful the discourse of Tcl. For instance, whenever needed, you tin make a fully-qualified sanction to immoderate of the object's methods wrong its namespace utilizing mymethod. This is basal for the commands that ever execute successful global scope, for illustration after. In C#, you mightiness usage a delegate aliases an arena handler, but location are nary "handlers" successful Tcl, only drawstring names, truthful immoderate book you springiness it to specified commands to eval arsenic callbacks must incorporate fully-qualified paths to commands and variables, for illustration so:
method schedule_update {} { # This generates a grip that 'after' tin usage later group callback [mymethod apply_theme]; # Generates '::oo::Obj42::my apply_theme' # After 5 seconds, tally the apply_theme method connected THIS object aft 5000 $callback }While modern frameworks adhd layers of achromatic box-like abstraction, Tcl keeps things simple. By treating each entity arsenic a bid and each bid arsenic a string, it eliminates the request for analyzable reflection, serialization, and vtables. It doesn't get successful your measurement because it has less ways to do so.
But, there's 1 important matter to support successful mind astatine each times: Tcl has nary car garbage collection! Therefore, erstwhile you create caller "objects" you can't simply "set a grip to null" and expect Tcl to cleanable up the command:
# Create the entity and shop its sanction successful the adaptable 'ac' set ac [AppConf new] puts "Variable 'ac' holds the bid name: $ac" # Verify the bid exists successful the interpreter puts "Does specified bid exist? [expr {[info commands $ac] ne "" ? "Yes!" : "No"}]" # Clear the adaptable 'handle' set ac "" puts "Variable 'ac' is now quiet (null-equivalent)" # But the bid is still there! puts "All progressive TclOO objects: [info commands ::oo::Obj*]" # If you cognize the sanction (e.g., ::oo::Obj42), you tin still telephone it manually: # ::oo::Obj42 helloHere's the output:
Variable 'ac' holds the bid name: ::oo::Obj42 Does specified bid exist? Yes! Variable 'ac' is now quiet (null-equivalent) All progressive TclOO objects: ::oo::Obj42Therefore, if you create a caller "instance" of a people wrong immoderate procedure, it will not get cleaned up arsenic soon arsenic the process finishes! You must explicitly telephone $my_obj destroy to make TclOO cleanable up the bid and its namespace without a trace.
Wait a minute… trace! We tin utilize it to adhd a "managed" measurement to instantiate "objects" and person them "clean up aft themselves"! We'll spot really to do this in a later section.
On the agleam broadside — pinch Tcl you're efficaciously equipped pinch a deterministic demolition model, akin to C++'s "delete" aliases Rust's "ownership", but wrapped successful the syntax of a high-level script. There's nary inheritance thread "scanning" exertion memory, truthful if you don't destruct it, it stays there. But if you do, it’s gone instantly.
All successful all, I personally for illustration coding successful Tcl pinch TclOO. It "abstracts away" a batch of "Tcl'isms" and house-keeping boilerplate. Old-school Tcl purists mightiness telephone this blasphemous, but to maine — this is the measurement to go. Besides, the Tcl Core Dev Team did make TclOO an charismatic portion of Tcl aft all, truthful possibly there's a bully logic for that?
Why Isn't Tcl More Popular?
Let's return a short breather and contemplate a little:
How travel Tcl (not conscionable Tk) isn't much celebrated today?
Well lets see: Tcl is BSD-licensed, lacks a centralized package repository, and moreover if it did, the simplicity of its root codification intends that galore adopters (in the firm world especially) would tailor Tcl's root codification to supply different sets of commands to suit their needs. But yet it boils down to 1 cardinal factor:
There's nary money to beryllium made by promoting Tcl/Tk.
People and companies who usage it are good alert of what a powerful instrumentality they person connected their hands, whilst deficiency of "casual coder community" reduces visibility of modern Tcl features for illustration TclOO, and yet stifles wide adoption. The truth that there's nary 1 sweeping the web to correct "wrong opinions" is because Tcl instauration has amended things to do. You know, for illustration improving the connection and fixing bugs. They besides don't person clip to beforehand Tcl "to the masses" because it makes small consciousness to do so. People mostly observe Tcl erstwhile they stumble upon Tk, and past they either softly adopt it aft reference books and docs, aliases get spooked by Tcl's unfamiliar command-like nature.
As for the users, Tcl is simply a workhorse successful EDA (Electronic Design Automation) and high-end networking, wherever the group utilizing it are besides engaged making 💲 💲 💲 pinch Tcl, without having to warrant aliases advertise their usage of it to anyone. When you are designing a microchip pinch 10 cardinal transistors, you don't attraction astir "UI trends" aliases GitHub stars. You attraction astir Zero-Regression and request a connection that worked successful 1995 and will activity successful 2045 because the costs of a correction is simply a $100 cardinal "tape-out" failure. If anything, I'd reason it's successful the interests of large-scale Tcl adopters to enactment quiet. Why thief the title observe specified a powerful, unchangeable model erstwhile you tin conscionable support making slope successful peace?
I connected the different manus person nary conflict of interest, and want you to effort Tcl out. Hence this ginormous, novel-sized article connected Tcl 9.0.
Tcl is for illustration the electrical grid. Nobody tweets astir the powerfulness staying on. They only tweet erstwhile it goes out. Because Tcl doesn't break, it doesn't make the "outrage" aliases "how to hole it" contented that drives the hype cycles of JS aliases Python. Especially JS. Yeah, JS… On, boy, particularly JS…
Any business tin return the Tcl source, cook it into a $500,000 portion of hardware aliases software, and ne'er mention Tcl again anywhere. There’s nary GPL request to stock root codification aliases proprietary changes to the Tcl interpreter. Thousands of high-end products are "Powered by Tcl" nether the hood, and the end-user ne'er knows it.

Tcl existed, Tcl exists, Tcl will proceed to exist. It is complete 38 years old now, and it will outlive the immense mostly of hyped up languages and frameworks that regularly popular up present and there, trying to reinvent the wheel.
If you spell pinch Tcl, you will fundamentally adopt the robust and reliable Industrial Architecture of the Fortune 500 tech giants for your ain projects, while the remainder of the world plays "Framework Musical Chairs", losing their minds and trying to hide their tears from exhaustion.
Practical Tcl/Tk Use Case — Decent Espresso
However astonishing Tcl/Tk is for automation and systems orchestration, you'll seldom spot it openly and deliberately utilized successful user products. Which makes the pursuing illustration moreover much fascinating.
Decent Espresso, the makers of the celebrated Decent Espresso DE1 Series java machines developed their Decent Espresso Machine software successful Tcl/Tk.

Even the Windows type is not packed into an EXE, and alternatively distributed arsenic a ZIP to beryllium extracted location and launched pinch the decent.bat file, which successful move executes the main.tcl book pinch the bundled undrowish standalone Tcl/Tk interpreter:
undroidwish\undroidwish-win32.exe src\de1plus.tcl -sdlwidth 1280 -sdlheight 800 -name Decent
But it's the Android type that's special. It tin beryllium tally pinch Androwish — Android app which tin tally GUI Tcl/Tk scripts and interface pinch mobile instrumentality systems and APIs — everything from the accelerometer and compass to Bluetooth. Currently the Android type of the package is the only 1 that tin really link to the existent espresso machines via Bluetooth, and is utilized arsenic the main interface for the devices.
"Generic", aliases platform-agnostic type is besides disposable for those readying to tally their Tcl scripts successful exotic OSes for illustration Haiku utilizing undroidwish. With Tcl the entity is the limit. If you tin larboard the expert somewhere, aliases moreover amended — to an OS/device pinch immoderate benignant of a show and an API to interface pinch it, you could tally CLI and GUI Tcl scripts pinch minimum changes.

In 2019 their lead programmer gave a talk directed toward programmers astir the Decent Espresso machine, and particularly the Tablet App.
One of the talking points was: "Surprising findings really Tcl outperforms competing different programs (in different languages) trying to do akin things" — among those was the truth that Tcl is unfastened source, and provides platform-agnostic UI API, which is elastic capable to build wholly customized event-driven interfaces, for illustration those recovered successful Decent Espresso Software. Opting for Tcl intends that the full app is ever represented by the root code, which makes it imaginable to do in-place changes to prevention clip and money. Instead of tweaking it connected the tablet, changes tin first beryllium done connected immoderate of the supported desktop platforms and tested successful undrowish, to get the closest practice of really the app would activity successful Androwish connected a existent Android tablet.
Why Choose Tcl/Tk?
Decent Espresso is genuinely unsocial because they went pinch Tcl/Tk successful AndroWish for a UI "framework".
Most instrumentality manufacturers would debar this because:
- It’s easier to find a React developer than a Tcl 1 erstwhile deciding connected a UI framework. To "run" a web app each you request is simply a instrumentality pinch a browser and entree to the section network. This wholly negates the "I can't tally this connected my iPad" problem Decent Espresso person been facing since time 1
- Consequently, if you spell pinch web-based tech, you will request to equip your devices pinch WiFi- aliases Ethernet-capable web cards aliases microcontrollers, and found communion connected LAN alternatively of Bluetooth…
- …which would make a modulation from a LAN-only to a cloud-based solution smoother…
- …which would, of course, simplify the preamble of vendor locks and paid subscriptions
- PROFIT!
Decent Espresso's prime of Tcl/Tk is simply a mediate digit to the full exemplary of specified a "walled garden". It is simply a local-first architecture wherever the tablet is simply a adjacent to the machine, not a customer of a distant server.
Mad. Respect.
Realistically though, Decent astir apt uses Tcl/Tk because their lead dev (John Buckman) simply loves the connection and the velocity of loop it allows =)
There's a bully grounds of that, arsenic he gave a talk connected this very taxable astatine the EuroTcl2019 conference. It's fundamentally the aforesaid position arsenic the 1 connected the Decent Espresso channel, isolated from astatine the extremity he's asked really DE deals pinch the truth that Android tablets tin person wildly different surface resolutions, and Tcl/Tk doesn't really support adaptive rasterized graphics. There, John Buckman confirms that this is simply a problem, and images request to beryllium "physically" resized first, earlier Tk tin make usage of them. Which is simply a very bully mentation of why different companies don't usage Tcl/Tk successful specified a way.
Most companies want their machines' UIs to look for illustration "iPhone apps", truthful they default to web-based tech, which is besides very mature. There's an abundance of tin UI frameworks that utilize the browser to do the heavy-lifting and dynamically accommodate graphics to different resolutions. So DE had to earnestly reinvent the instrumentality here, and travel up pinch their ain tool-set for a skinnable, full-screen UI that would look for illustration a modern app without relying connected default Tk widgets. This is impressive, particularly considering the situation of making a window-oriented Tk toolset easy to usage for consumers, connected a touch-controlled instrumentality for illustration an Android tablet.
Nowadays though, sloppy of really overmuch I for illustration Tcl, I personally would deliberation doubly earlier committing to Tcl/Tk for specified a usage case. IMO, if you don't person 5+ years of an existing method debt, it's conscionable overmuch easier and much reasonable to spell pinch a "modern default" where:
- Since we're not surviving successful the acheronian ages, we tin equip the instrumentality pinch a inexpensive yet tin microcontroller for illustration ESP32-C5. This would let it to link to the section web and usage the TCP/IP stack for communication, aliases moreover create a WiFi entree constituent pinch WiFi Direct for different devices to link to it directly
- The aforesaid microcontroller, pinch the thief of the powerful real-time OS it comes pinch (RTOS successful lawsuit of ESP32) tin now do it all:
- autonomously negociate the device's functions
- allow to present beingness controls for illustration buttons and levers
- can connection a simplified interface connected a built-in, I2C-connected LCD screen
- is tin of mounting up a headless HTTP/HTTPS REST aliases WebSockets API server for distant control
- And… Now you tin power your instrumentality from anything, and successful immoderate measurement you want: create a autochthonal mobile app, aliases a web app pinch immoderate of the galore JS frameworks, aliases conscionable nonstop JSON packets to the API manually. You're not locked into a peculiar framework. As for Tcl/Tk, now you tin really usage it for what it’s champion at: quickly building a data-heavy Engineer's Dashboard
- BONUS: ESP32-C5 MCU supports Bluetooth 5 LE, truthful you tin ever usage BT arsenic a secondary/fallback connection protocol for cases wherever the aerial is excessively engaged moreover for a 5HGz WiFi network, and utilize it from a autochthonal Android/iOS app. In which lawsuit you'd person all bases covered, future-proofing your instrumentality moreover further
BTW, if you're funny really a microcontroller for illustration ESP32 could beryllium utilized to do each of the supra — check retired my blog station connected this very topic.

As of April 2026, the DE1 app is still a Tcl/Tk exertion that runs wrong Androwish. Here's a reasonably caller video explaining really the app works. Watch the first mates minutes to spot it successful action.
I will besides spoil the "secret" which makes DE1 app truthful soft connected Android, moreover though technically Tk lacks immoderate shape of hardware acceleration. The Decent app runs connected Android utilizing AndroWish, which relies connected SDLTK. Instead of utilizing the accepted CPU-bound X11 aliases Win32 drafting calls, SDLTK replaces the full backend of Tk pinch the Simple DirectMedia Layer (SDL2), which is hardware-accelerated! By mapping Tk's drafting commands to SDL2 textures, the full UI gets a capacity boost from the GPU. This is why the Decent app tin grip high-frequency information visualization connected a comparatively humble Android tablet.
Finally, "Decent Software" is simply a package package chock afloat of absorbing .tcl scripts. Feel free to download the bundle and cheque retired the code, afloat of developer comments for illustration this one:
package require crc32 catch { # john 6/17/2024 not judge why this is moreover included, arsenic it's not utilized by immoderate codification I tin find. package require BWidget }This 1 successful peculiar is interesting, because John Buckman utilized to mention megawidgets respective times successful his talks, but seems for illustration they were being slow phased retired successful favour of autochthonal Tk widgets. Which makes sense, arsenic BWidgets and galore accepted "megawidget" frameworks are functionally obsolete for modern Tk development. While they still activity (again, Tk provides legendary backward compatibility), their relevance was mostly gutted by the preamble of the Themed Tk motor successful type 8.5.
For instance, here's what you'd usage alternatively of BWidget widgets if you were to build a GUI app today:
- Instead of BWidget::NoteBook you'd usage ttk::notebook
- Instead of BWidget::Tree — ttk::treeview
- And BWidget::ProgressBar is now replaced by ttk::progressbar
Finally, statement really huge immoderate of the scripts are, arsenic good arsenic the sheer number of them successful the archive. Yet different proof, that Tcl tin construe analyzable scripts pinch tons of dependencies, and is performant capable to chew correct though them during execution.
Tcl/Tk connected Android With Androwish
Now astir that "Androwish" app Decent Espresso are using... What is it anyway?

Androwish is an Android app that tin tally Tcl/Tk scripts wrong its ain windowing environment. It comes "batteries included", and supports specified important packages arsenic sqlite3, tls, Thread and galore more, including platform-specific ones for illustration borg, utilized to pass pinch the Android strategy APIs.
As of May, 2026, Androwish comes pinch Tcl/Tk 8.6 support only. It's still plentifulness for performing almost immoderate task you'd usage Tcl/Tk for, but do support successful mind that this intends that definite commands introduced pinch the newer versions, for illustration Tcl 9's dict getdef will beryllium unavailable. So scheme ahead.
Lastly, while we're connected the taxable of moving scripts connected Android, successful summation to those recovered on the charismatic website, I'd for illustration to stock immoderate cool facts and tips astir Androwish that aren't obvious, but show really it's overmuch much than conscionable an "Android Tk wrapper":
- After installing Androwish make judge to spell to the database of its permissions and give it entree to the files connected the device. Afterwards, spell to: File => Source => Type "sdcard" successful the filename section and property "Open" — voia! You tin now browse the files connected the instrumentality and tally Tcl scripts from disk
- By default, Tk apps wrong Androwish will simply look for illustration desktop windows moving connected an Android device. It mightiness beryllium thing you're after, but it makes consciousness to maximize the app to easy make usage of the afloat surface real-estate, arsenic you saw successful my Mortgage Calculator Tk showcase.
- With Androwish, it's imaginable to create and administer your ain .APK app installers pinch embedded scripts, which fundamentally enactment for illustration apps, wherever Androwish will execute the bundled "main.tcl" record likewise to really a Starkit aliases a Zipkit would (we'll look astatine Tcl 9 "Zipkits" further down the post). For this, usage the Androwish SDK. The SDK comes bundled pinch a graphical instrumentality called "bones". The "default" Androwish APK installer is immense (~30MB) because it includes everything. The bones instrumentality allows you to uncheck extensions you aren't utilizing to easy shrink your APK down to ~4-10MB.
- You tin quickly motorboat your scripts from the browser aliases immoderate different app that supports URLs. For that, usage a pursuing prefix: androwish:///sdcard/ — and simply append it pinch the afloat way to the book you want to run! For example: Documents/mortgage.tcl will trigger an Intent that launches Androwish arsenic a abstracted Activity, bringing it to the foreground to execute the script. Then, conscionable for illustration pinch different autochthonal apps, erstwhile you exit the Tcl book aliases property the backmost fastener (as agelong arsenic you hindrance the <Key-Escape> to the exit command), the strategy stops the existent activity and returns you to the erstwhile 1 — the browser, aliases a note-taking app etc.
- Androwish comes pinch the "borg" extension that tin beryllium utilized to telephone autochthonal Android functions: power Bluetooth functionality, nonstop OS notifications (including instrumentality vibration and moreover speech), location information, etc. You really tin create your own, powerful Android CLI/GUI tools pinch conscionable Tcl/Tk without ever having to touch Android Studio! Just ideate what you're getting for free, wholly unfastened source:
- No Compilation — you conscionable constitute a .tcl matter record and require communal packages
- Live Testing — prevention the file, and Androwish runs it instantly. Need to make changes? — Open the record up connected the phone/tablet utilizing immoderate matter editor and alteration to your heart's content
- The "Borg" Bridge — usually, to usage Bluetooth aliases GPS, you'd request to constitute a batch of Java boilerplate code. Borg is simply a pre-compiled span that lets you enactment wholly wrong the Tcl expert and still person entree to autochthonal strategy calls
- And, naturally, arsenic I demonstrated successful my Mortgage Calculator demo supra — you tin person 1 cross-platform tcl book that could tally successful almost immoderate OS, including Android, and only package require and trigger Android-specific borg calls upon identifying the level arsenic "Android" by the beingness of this peculiar package
- Beyond borg, Androwish besides translates definite Android strategy changes into Tk Virtual Events, for example:
- <<NetworkInfo>> — Triggers erstwhile Wi-Fi drops aliases connects
- <<Accelerometer>> — Happens erstwhile the instrumentality is physically moved
- <<WillEnterBackground>> — Sent erstwhile the personification swipes distant from the app
- If you request to trial your scripts wrong the SDL-accelerated situation that Androwish provides without having to trial everything on device, cheque retired its related task — undrowish. It's a single-file Tcl/Tk binary for Windows (32 bit, optional 64 bit) and Linux utilizing parts of the AndroWish root tree, sans the borg extension. Comes "batteries included" arsenic well
- In fact… Instead of typing Tcl codification connected a mini Android keyboard aliases perpetually re-uploading scripts, you tin link your PC's keyboard and surface straight to the Tcl expert moving wrong the Androwish app connected your device utilizing tkcon, aliases "Enhanced Tk Console". Androwish includes a package called tkconclient. You tally a mini snippet of codification successful Androwish to commencement a listener connected immoderate larboard complete 1024, for illustration "12345": package require tkconclient; tkconclient::start 12345 — past connected your PC simply tally tkcon and show it to link to your phone's IP address, aliases via USB utilizing adb guardant — adb guardant tcp:12345 tcp:12345. Ta-da! You now person a model connected your PC. Anything you type location executes instantly connected the Android device's screen. Just make judge to usage this only during development, since tkconclient has nary built-in password
- Finally, with Androwish, you tin customize your startup environment truthful you don't person to manually type setup commands each clip you unfastened the app. To do this, Create a matter record pinch the codification you want to execute connected startup, and prevention it to your Android instrumentality astatine /sdcard/AndroWish/.wishrc
I myself person only scratched the aboveground of what the Androwish+Tcl/Tk combo could beryllium utilized for, and it judge seems for illustration the sky's the limit.
OK, the luncheon break is over! Let's dive backmost into the nutrient and potatoes of Tcl.
Tcl's Powerful Stackful Coroutines
A coroutine is simply a process that tin "pause" mid-execution, return a worth to the caller, and later "resume" precisely wherever it near off, preserving each section variables and the instruction pointer. If you travel from the world of crippled development, you're astir apt well alert of coroutines and must've utilized them astatine immoderate constituent to orchestrate scripted events, aliases to tally scripts connected a timer. In my game I utilized coroutines mostly for cutscenes of the Kristie's shape capacity sequences, wherever a azygous coroutine would enactment arsenic a "director", triggering systems based connected really good the subordinate did connected immoderate peculiar level, pinch definite events taking spot based connected the clip of time erstwhile the capacity would return place.
In the world of exertion development, coroutines are usually employed to break agelong operations into chunks, for illustration downloading a ample file, aliases processing monolithic datasets, managing analyzable authorities machines, and handling concurrent web requests. They beryllium because it's not ever reasonable to break programme execution into existent processing threads, pinch each the complications that originate pinch managing shared states etc. So a measurement to stock a azygous thread betwixt the functions of a programme without freezing different operations is needed, and coroutines supply precisely that.
Tcl supports coroutines retired of the box. And they tin beryllium utilized almost precisely arsenic successful JavaScript, Python aliases C#: non-blocking erstwhile idling, efficient, executed successful a queue of different coroutines successful the aforesaid thread.
This brings the powerfulness aliases "multi-stage" procedures to CLI and desktop apps.
proc loop_coroutine {} { for {set one 1} {$i <= 100} {incr i} { puts "Step $i" aft 50 [info coroutine] yield } group ::done 1 ; # Signal that we are wholly finished } # Create the coroutine coroutine task1 loop_coroutine # Start the Event Loop and hold for the 'done' variable # BTW: In a Tk app theres is nary request to usage vwait. The wm (Window Manager) keeps the arena loop moving everlastingly until the model is closed. In a CLI app ydo ou request vwait to enactment arsenic the anchor that keeps the process from floating distant while inheritance tasks are doing their thing. vwait done puts "Coroutine Process Complete!"But that's not the full story.
Tcl uses Stackful Coroutines (as do Lua, Erlang and Go).
In contrast, C++, C#, JavaScript, and Python instrumentality Stackless Coroutines. They trust connected the modular LIFO CPU stack, wherever a usability cannot "pause" and stay connected the stack. Any process must return power to the caller and beryllium popped off. So successful these languages the compiler/interpreter really rewrites coroutine functions into heap-allocated objects because it cannot time off a "hole" successful the strategy stack. And since stackless functions must return to pause, every usability successful the telephone concatenation must beryllium "stackless-aware", that is — marked async.
Sound familiar?
In this regard, Tcl doesn't attraction astir the "C Stack". When you telephone yield, Tcl snapshots the full telephone stack for that coroutine. So you tin telephone Proc A => Proc B => Proc C, and if Proc C calls yield, the full concatenation pauses. This attack is incredibly difficult to instrumentality well. To make stackful coroutines work, the authors of the connection must constitute a civilization virtual instrumentality that tin "detach" and "reattach" segments of representation dynamically. It’s a marvel of package engineering, and successful Tcl it's called "NRE" — a Non-Recursive Engine. It was introduced successful Tcl 8.6 specifically to lick the problem of pausing execution mid-procedure.
Most modern languages (JS/Python/C#) chose the "Stackless" way because it's easier to bolt onto an existing language, moreover though it forces the programmer to woody pinch async/await boilerplate everywhere.
Such "academic" coroutine implementation is objectively 1 of the astir blase successful the industry, often compared only to Lua's and Go's. Which explains their communal usage cases:
- Lua successful Roblox, World of Warcraft, and Lua-based engines (like LÖVE) — In Roblox for instance, each book you constitute for a portion is simply a coroutine. When you telephone task.wait(), you are triggering a stackful yield. A crippled motor cannot spend 10,000 threads. It tin spend 10,000 coroutines which inhabit a fewer kilobytes of representation each, and don't require "Context Switching" astatine the Kernel level. Here's proof
- Tcl successful Cadence, Synopsys, and Siemens Software — arsenic a full astonishment to no one, successful EDA apps of billion-dollar-valued companies billions of transistors request to beryllium simulated. Tcl's output implementation allows the simulation to region a book while the physics motor calculates the electrons, past resume the book precisely wherever it near off
- Go’s successful the cloud/server world — Go's occurrence successful this area is mostly a consequence of it solving the problem that plagues C# and JavaScript, because… Go has nary async and await keywords! It doesn't request them. Go is really the astir precocious successful this regard, because it tin region a coroutine astatine immoderate moment, whereas pinch Tcl and Lua the codification must explicitly opportunity output to walk the execution complete to the adjacent process
- (Side-note) In regards to C# successful .NET 11 — things are improving somewhat pinch the caller "Runtime Async" approach, wherever alternatively of the compiler generating a massive, hideous IAsyncStateMachine authorities instrumentality struct, starting pinch .NET 11 it marks the method pinch a marque caller flag. The JIT past compiles it arsenic a caller "resumable method". Thus, the updated .NET runtime itself tin negociate the suspension, which should astatine slightest boost wide capacity of each async-aware code. But fundamentally C# still is, and will stay stackless for the foreseeable future. So please, don't confuse my talking astir this creation trade-off pinch bashing C++ aliases C# for it! Stackless makes consciousness for a systems connection aimed astatine maximum efficiency, truthful specified prime is simply a sensible compromise. Tcl coroutines are more costly successful position of CPU and memory because they support a full stack alive, but they are cheap and convenient for the programmer because you don't person to alteration your codification building and "color" functions arsenic async each the measurement up the codification chain. C# "coroutines" (Iterators/Async) are inexpensive for the CPU because they don't clasp onto a existent OS thread aliases a afloat representation stack, but they are awkward for the programmer because they unit you to colour each usability successful the concatenation arsenic async and telephone await to match
In the pursuing illustration the full series is started arsenic a azygous coroutine. The nested process wait_ms is capable to woody pinch the output connection arsenic a consequence of that, triggering the aft timer by passing the sanction of ::fullcoro coroutine arsenic a book to beryllium tally aft a specified delay, yielding until that timed call, and past returning the execution backmost to the animated_type proc. Resulting successful the "animated type" effect:

Finally, Tcl's tcllib modular room contains the coroutine package, which provides coroutine-aware wrappers for blocking I/O — socket ,gets aliases publication pinch its coroutine::util ensemble. With these, you tin execute your www-file downloads aliases ample record sounds successful the main thread without blocking it entirely, by automatically yielding power to the arena loop erstwhile information is not ready. This allows the main thread to stay responsive (handling GUIs aliases different tasks) while the coroutine waits for I/O.
If that's a spot excessively low-level for doing downloads — the http package from the aforesaid modular room has you covered. It is arena loop resident arsenic it uses a inheritance arena loop to watercourse information to a specified -channel. So it's besides built to beryllium non-blocking.

Although very useful, coroutines still person to stock a azygous CPU thread. If immoderate cognition takes a agelong clip — seconds alternatively of microseconds — specified an arena would lead to the full app (including the UI) freezing until processing is complete. For specified cases Tcl provides different bullet-proof solution:
Threading successful Tcl
Threading is an precocious topic, and threads should ideally beryllium utilized only erstwhile perfectly basal owed to the added request to power respective concurrent instructions taking spot successful a azygous app. Parallel programming is notoriously difficult for a assortment of reasons: title conditions, deadlocks, threads becoming unresponsive etc.
These problems are commonly caused by threads sharing the aforesaid representation space, wherever they each person entree to the aforesaid information — In C++, C#, Java you request to beryllium very observant and employment semaphores to guarantee only 1 thread tin entree a azygous portion of information astatine the aforesaid time. When programming for ESP32 I had a good clip utilizing semaphores to supply a safe shared entree to the aforesaid LCD surface for my animation routines, to debar the surface crashing owed to an on-going I2C bid being interrupted mid-way by different instruction.
In contrast, Tcl threads instrumentality a alleged Shared-Nothing aliases "Apartment Model" via the Thread package wherever each thread gets its own, wholly isolated interpreter.
As a consequence of specified an architectural choice:
- No shared variables
- No title conditions
Communication and information sharing are only imaginable via:
- Message Passing — sending a book to different thread to execute pinch the thread::send command. This is the main measurement to pass betwixt threads utilizing their IDs
- Shared Memory Containers — chiefly TSV (Thread Shared Variables), provided by the Thread package. Unlike a modular C# adaptable wherever you constituent to a representation address, tsv commands are internally protected by mutexes. All you do is telephone the provided commands from the TSV namespace to get, group aliases alteration values, and each cognition will beryllium guaranteed to beryllium atomic, automatically managing queuing erstwhile respective threads effort to entree aliases alteration the aforesaid shared variable. TSV variables are not autochthonal Tcl variables, you can't put a trace connected them for instance. But they do supply certain commands to atomically run connected information arsenic if it were a string, a list, an array, aliases a keyed database (like a dictionary)
- Shared Channels — In C# aliases C++, you tin walk a socket grip to aggregate threads. However, if 2 threads telephone write() connected that socket simultaneously, the information tin get interleaved aliases corrupted astatine the OS level. You person to wrap the socket successful a fastener to forestall this. Tcl forces information by design, truthful you can't entree the aforesaid transmission from respective threads. You request to walk the transmission betwixt threads using specified commands arsenic thread::transfer, thread::detach and thread::attach
For control, by default each thread enters an arena loop and tin only beryllium controlled via messages. Therefore, moreover erstwhile you request to execute immoderate dense processing successful a kid thread, it's a bully thought to either break the task into chunks, aliases astatine slightest make judge the thread signals backmost to the genitor upon task completion.
# In the Main Thread: thread::send -async $workerId { # This will hap successful the kid thread interpreter's scope group consequence [heavy_calc] # Callback to study backmost to the main thread thread::send -async $mainThreadId [list process_results $result] }Tcl makes threading safer, but it can't protect you from bad concurrent code.
If a thread freezes — there's nary measurement to "kill" it. This is because a thread owns an interpreter, truthful it whitethorn person unfastened files (or channels), aliases beryllium changing a shared variable. If you were to "kill" the OS thread, the Tcl room wouldn't beryllium capable to cleanable up the soul C structures, starring to a corrupted process authorities aliases an contiguous clang of the full application. So If your kid thread enters an infinite loop for illustration while {1} {}, that thread is "lost" to your exertion until the process exits. It will beryllium location consuming 100% of a CPU core, and nary thread::send will ever scope it because it ne'er returns to the arena loop to cheque its messages.
Finally, arsenic Tcl threads don't stock memory, the Tcl expert doesn't request a Global Interpreter Lock (GIL) dissimilar Python. This intends Tcl scales linearly, and connected a CPU pinch 16 identical cores:
- If 1 thread tin do 1000 operations/sec,
- 16 threads will do astir 16000 operations/sec. Python often struggles to execute specified scaling
To study astir the Thread package and Tcl threading, dowbload a free PDF connected Threads astatine Magicsplat.com, generously provided by Mr. Nadkarni. Look for "Chapter 22. Threads". It will thief you get a grip connected Tcl threading and screen different useful Thread package goodies for illustration Thread Pool — tpool.
Threading Demo
Just for fun, let's measure representation ratio of Tcl threads.
Here's a CPU stress-test written successful Tcl: 💾 Download script

If you tally it and activate the test, note really the main model stays responsive moreover erstwhile CPU load hits 100%. This is because the hefty ram polling process is executed successful a abstracted thread and sends information via messages to the main thread which manages the UI. To verify, effort moving the execution of RAM polling backmost to the main thread and observe really the model stutters each 500ms while you resistance aliases resize it.
On my 16-core, 32-thread AMD 5950X machine, aft spawning 32 kid threads RAM usage went from 24MB to 44MB. How businesslike is this really?
To put that into perspective, let's comparison Tcl to different interpreted languages:
- Python: Spawning 32 processes (since threads can't do existent parallel CPU activity successful Python owed to the GIL) would costs astir 320MB to 600MB minimum
- Node.js: 400-800MB. Each "worker" is simply a caller V8 isolate, lighter than a afloat process, yes, but V8 is simply a memory-hungry JIT motor designed for speed, not representation efficiency
- Ruby: 300–500MB. Similar to Tcl interpreters but pinch a overmuch larger object-header overhead and a much analyzable garbage collector
- Lua: 15–30MB. Lua was designed for embedding successful C apps (like games) wherever representation is astatine a premium, and it shows! Alas, Lua does not person a built-in "Thread" module. To get existent OS-level threading you request to get and compile third-party C-libraries for illustration Lanes aliases llthreads2. It's is really much fresh for embedding into existing package for illustration games, arsenic the LuaJIT Just-In-Time Compiler bakes basking codification paths and loops into autochthonal instrumentality codification connected the alert truthful effectively, that you often don't request to interest astir threads astatine all. That's why it's a connection of prime successful the Defold crippled engine, celebrated for its mini output binary sizes, and precocious performance
What astir the "big boys"?
- C# (.NET): 150–300MB. Extremely businesslike execution, but the CLR (Common Language Runtime) has a precocious RAM overhead. The JIT compiler and GC metadata for 32 progressive threads besides return up immoderate space. Not excessively shabby for a powerful managed language, though
- Java (in JVM): 256–1GB+. The JVM is simply a notorious representation hog, lol. Even pinch Virtual Threads the resident group size (RSS) stays ridiculously precocious owed to heap management
- Go: 20–30MB. Go is simply a connection pinch beautiful safe threading via the M:N scheduler and is so very good fresh for high-traffic multi-threaded backends, so… Go, Go!
And Tcl, for the afloat picture:
- Tcl: 20MB. That's only ~0.6MB per "worker" (as successful — a abstracted interpreter)
Such representation ratio is mostly imaginable acknowledgment to the complete deficiency of JIT compilation. C#, Node, Java construe bytecode into instrumentality codification astatine runtime and cache it successful a "Code Cache" successful RAM. Doing activity successful 32 threads leads to bloated RAM cache. In contrast, Tcl is strictly a Bytecode Interpreter, written successful highly optimized C. It compiles a merged book into soul bytecode once, and past steps done it.
So each successful all, Tcl is hitting almost C levels of representation ratio while remaining a high-level interpreter.

High Memory Efficiency Doesn't Imply Maximum Performance
Dial backmost and statement really I opportunity "memory efficiency".
Tcl was conceived arsenic a "glue" language, not 1 meant for compute. On a modern multicore CPU, you could usage it for dense math, but it would beryllium a elephantine discarded of CPU powerfulness still. It’s overmuch slower successful earthy execution velocity than Rust, C aliases C#, of course. But nary 1 should usage immoderate high-level interpreted connection for dense number-crunching tasks for illustration compression aliases AI exemplary training/inference anyway.
To corroborate this, let's measurement the single-core capacity quality betwixt AoT-compiled C# app (native code) and a Tcl script, calculating quadrate guidelines 100mil times connected an AMD 5950X CPU moving astatine a fixed 3600MHz.
// 100 cardinal iterations (C#) for (int one = 0; one < 100_000_000; i++) { consequence += Math.Sqrt(i); } # 100 cardinal iterations (Tcl) for {set one 0} {$i < 100_000_000} {incr i} { group consequence [expr {$result + sqrt($i)}] }- C# — 0.24s. Native C-level performance. As accelerated and businesslike arsenic the silicon allows
- Tcl — 16.8s. And this is pinch Tcl compiling expr to bytecode. Would've been moreover slower otherwise!
70x capacity difference for doing tons of math. Even if you saturated 16 threads successful Tcl, a single core of autochthonal C# codification would still decorativeness the occupation astir 4.5 times faster than each 16 Tcl threads combined.
Therefore, conscionable really it says successful the Tcl root codification description, the correct measurement to deliberation of threading successful Tcl is not arsenic a rival to C#'s System.Threading.Tasks, aliases Go’s goroutines, aliases Rust’s Fearless Concurrency, but alternatively as a instrumentality for guaranteed UI responsiveness and asynchronous orchestration. Imagine doing a long-running inheritance task — for illustration downloading tons of files aliases reading/writing to tons of channels/devices successful the background. Use threading truthful your app model doesn't freeze, while being capable to query the position of the on-going task to show it connected a advancement bar. Or, if your Tcl instrumentality runs successful the console — tie an ASCII spinner pinch a coroutine, aliases proceed dispatching tasks while waiting for respective kid treads to decorativeness their heavy, thread-blocking activity and return results utilizing callback messages, the event-driven way.
Tcl is astir concurrency (doing many tasks astatine once), and not parallelism (doing one large task faster). Still, if you really want to scope C-like performance-levels consecutive from Tcl, location is simply a solution…
You tin embed C codification into your Tcl scripts.
Script-Embedded C? Script-Embedded C!
Tcl has a package called critcl — Compiled Runtime for Tcl — which allows you to embed C codification straight wrong your Tcl scripts. And it compiles it connected the fly, once. You constitute a snippet of C, and Tcl handles the compilation, linking, and loading automatically. This, for instance, allows accessing Tcl_Obj soul practice struct, which the C codification sees arsenic earthy bytes successful memory. NaviServer, a web server written successful Tcl/C, is designed astir this very concept.
As a speedy demo — successful "raw" Tcl, a loop for calculating a Mandelbrot group aliases a ample Fibonacci series is slow because each loop is an interpreted command. With critcl, you tin constitute a performance-critical loop successful C, and it behaves for illustration a autochthonal Tcl command.
package require critcl # Define a C usability that becomes a Tcl command critcl::cproc fast_fib {int n} agelong { if (n <= 1) return n; agelong a = 0, b = 1, tmp; for (int one = 2; one <= n; i++) { tmp = a + b; a = b; b = tmp; } return a; } # Now telephone it for illustration a regular Tcl proc puts "Fibonacci 45: [fast_fib 45]"With this, you'd fundamentally beryllium creating a DLL/Shared Object connected the fly. You tin moreover embed C++ codification pinch critcl::config language c++ — otherwise, the workflow remains the same.
Of course, it's not each "write and forget" arsenic critcl does require a C compiler (gcc aliases clang) to beryllium coming connected the instrumentality the first clip the book runs. So if you administer this to a personification without a compiler, it fails. The on-the-fly compilation is simply a development feature. It's meant to springiness devs the expertise to person an all-in-one Tcl book for prototyping. Once the codification is finished, you would cook it into a binary. So you'd usage critcl to make a shared room (.dll connected Windows, .so connected Linux) connected your machine. Then administer that room alongside your book without having to alteration the book itself:
- Run critcl -pkg yourscript.tcl connected the dev machine
- It generates a binary package folder
- You vessel that files pinch your Tcl script/app
Now, to beryllium honest, this is simply a very old-school attack to app development. Unless you really cognize your measurement astir C aliases C++, and are disciplined and motivated capable to jump though hoops to straight activity pinch Tcl runtime representation structures, you should astir apt look for different measurement to supplement your Tcl apps pinch the expertise to do dense math.
Recall that Tcl is simply a versatile "glue" connection for GUI and CLI apps. So why not glue it to thing else, written successful a modern, managed language, overmuch amended suited for a domiciled of a versatile "powerful calculator", while your Tcl UI does what it is meant to — negociate and display.
Such an attack is known arsenic a "Thin-Client UI" aliases a "Sidecar Pattern".
The "Thin-Client UI" Use Case Example

There is simply a myriad of ways to do this, but erstwhile it comes to specified a usage lawsuit — Tcl + Child Process — I personally spell pinch a Tcl/Tk + C# combo pinch a twist:
I compile my C# apps into autochthonal codification utilizing BFlat.
Bflat produces portable executables which don't require .NET to beryllium installed connected the target platform. With an added prize of being capable to scope C-level capacity without having to really use C aliases C++, while retaining entree to C#'s powerful threading capabilities and its garbage collector! And the resulting binaries are mini acknowledgment to zero .NET bloat. In fact, you don't moreover need the .NET runtime aliases .NET SDK installed connected the build strategy astatine all, arsenic BFlat comes pinch precompiled versions of communal dependencies. You conscionable constitute your C# codification and compile it pinch BFlat for illustration you would pinch C++ and clang++. And past tally it anyplace arsenic a self-contained portable executable for Windows, aliases a binary pinch minimal limitations (standard libc) that runs crossed almost immoderate Linux distro. You tin moreover build binaries for Android!
For example, a number-crunching C# CLI app that uses "basic" .NET libraries tin beryllium compiled into a mini 900KB CLI executable for Windows. And if you tally it done the UPX compressor, the resulting CLI app will beryllium nether 500KB successful size! A non-bloated, tiny, AoT-compiled C# binary that carries its ain garbage collector and runtime pinch a mini footprint. Scandalous!
Then, for a 100% cross-platform instrumentality package I would compile 2 much binaries — for Linux and macOS — and spot those into the bin/ subfolder adjacent to my script. My Tcl "Glue" app would past observe those and execute the due binary depending connected the detected platform. As a last accord, I could spell moreover further and battalion the full instrumentality into a standalone binary, making bully usage of the Zipfs filesystem Tcl 9.0 supports, to bundle the compiled C# binaries inside. These would past get extracted into a directory (bin/ aliases strategy tmp/) and spawned arsenic kid processes.
Total app size? Can beryllium arsenic mini arsenic 3-4MB:
- 3MB for the "pure" Tcl expert without other packages
- 500KB for the C# "child binary" compiled for the target level pinch minimum .NET deps
And arsenic a consequence I get Native performance, Cross-platform reach, and beryllium Zero royalties aliases licensing fees for some individual and commercialized usage of my package without GPL-like "copyleft" requirements, because Tcl is BSD-licensed, BFlat is MIT and .NET Runtime components utilized by BFlat are MIT/Apache 2.0.
Being event-driven astatine the core, Tcl/Tk is cleanable for specified usage case, arsenic your UI will enactment responsive while waiting for dense mathematics to beryllium done by the kid process, utilizing Tcl’s arena loop called fileevent which natively handles non-blocking I/O from pipes.
# Tcl treats a tube to a C# process precisely for illustration a web socket aliases a file # Mandatory cheque for the extremity of the file. Otherwise, erstwhile the C# app closes, the readable arena will trigger infinitely because "EOF" is technically a readable state proc handle_output {pipe} { if {[eof $pipe]} { drawback {close $pipe} puts "Child process finished." return } group information [gets $pipe] puts "Received from C#: $data" } set tube [open "|[list ./native_app.exe]" r+] # By default, Tcl buffers output. If our piped app sends a statement but doesn't adjacent the stream, Tcl mightiness hold for much information earlier triggering the event. Adding '-buffering line' to fconfigure is usually a bully thought for specified a usage case fconfigure $pipe -blocking 0 -buffering line fileevent $pipe readable [list handle_output $pipe]In the previous, threaded CPU accent trial illustration the Tk UI stayed perfectly responsive moreover erstwhile the CPU was being hammered astatine 100%. Similarly, the UI and the kid Process are logically separated by a tube connected the OS-level, truthful it is intolerable for a C# app clang aliases a 100% CPU spike to "hang" the Tcl interface, vastly improving personification experience.
Now there's moreover much inducement to effort and utilize Tcl/Tk to constitute portable devices aliases graphical personification interfaces, wouldn't you agree? And it gets moreover better, because…
You tin battalion Tcl/Tk scripts arsenic standalone apps!
Standalone Apps. Starkits and Zipkits

Zipkits… Starkits… Oh boy, what a travel it's been figuring those out!
Having vanished yet different awesome caller Tcl/Tk tool, you'll yet see packing everything into a azygous executable to tally connected machines without Tcl/Tk preinstalled. Naturally, pinch Tcl being interpreted successful nature, this intends you'd request to battalion its expert (compiled for the target platform), your scripts, various libraries and media, to recreate the situation your programme needs to run.
How would you propulsion this disconnected pinch Tcl/Tk?
Tcl 8.6 and Starkits
Before Tcl 9.0 it would've been complicated: you'd person to trust connected alleged "Starkits" — containers for your scripts and files, which could beryllium appended to a Tclkit to shape a "Starpack", wherever "Star" is derived from STandAlone Runtime. These were mostly unofficial, self-contained executables built connected a database called Metakit. It was a proprietary achromatic box, and if it broke, you'd request specialized devices for illustration SDX (Starkit Developer eXtension) to execute observant "surgery" connected your binary…
Thankfully, this is nary longer the case, arsenic ZipFS support was officially implemented pinch the merchandise of Tcl 9.0. It became the modular no-nonsense measurement of building single-file binaries, and only requires 2 parts:
- The data — your scripts, civilization and modular Tcl/Tk libraries and extensions, and immoderate media files packed into a zip archive
- The runtime — a typical Tcl expert which, erstwhile attached to an archive pinch data, would detect, publication and tally your codification automatically, arsenic good arsenic provides the functionality to really execute the said stitching
Tcl 9 and ZipFS
If you return a normal ZIP archive and "mount" its contents successful Tcl 9.0, you'll get ZipFS — a read-only virtual filesystem, accessed via //zipfs:/ which tin beryllium publication conscionable for illustration immoderate different disk. It's a neat idea. The ZIP record format has been successful nationalist domain since 1989, supports storing files and directories, provides existent information compression, and is recognized by virtually each OS successful existence.
So why did the Tcl halfway dev squad hold for 27 years, until September 2024, to make this the modular pinch Tcl 9.0? — Backwards-compatibility and robustness. Exactly what Tcl is known and weighted for. As a matter of fact, Tcl 8.6 which was released successful 2012, already provided the tools to unzip files, but it didn't person the infrastructure to dainty the executable itself arsenic a mountable thrust retired of the box. That required a basal rewrite of the initialization sequence, aliases the codification that runs earlier the book motor is moreover awake.
Also, the Starkit+Metakit attack had served the Tcl organization good for complete 20 years, truthful location was nary urgency to switch.
…and past AndroWish happened.
When Christian Werner started the AndroWish project, he had to woody pinch the truth that Android apps are distributed arsenic .apk files, which are technically conscionable renamed ZIP archives. For Tcl to tally connected Android, the expert had to beryllium capable to someway "reach into its ain APK" to find its room scripts, images, and extensions. To lick this, Christian wrote a C-level Virtual File System (VFS) driver that allowed Tcl to dainty a ZIP archive arsenic a unrecorded directory. The caller driver was robust capable because it didn't trust connected analyzable outer database engines and utilized the zlib already coming successful the core, positive a minimal C-wrapper to navigate the ZIP directory structure. This codification was developed for AndroWish and its desktop related undroidwish, earlier yet being merged into the charismatic Tcl core.
So now, pinch Tcl 9 you tin "mount" immoderate compatible ZIP record arsenic a virtual system. Here's a random *.zip record that I created utilizing 7zip, and past mounted arsenic //zipfs:/mnt/ successful my elemental Tcl 9 ZipFS Inspector tool:

AndroWish and undroidwish utilize ZipFS to bundle their galore libraries, conscionable for illustration immoderate due Tcl 9 "batteries included" situation would. Here's a sample of what's packed into the ZipFS VFS of undrowish and Androwish, accordingly:

Finally, arsenic a consequence of ZipFS becoming a halfway portion of Tcl 9, we now person Zipkits — statically linked tclsh and wish binaries, which whitethorn beryllium utilized to create azygous record applications. And if you equine 1 arsenic a ZipFS source, you'll spot the directory building of a "standard" Tcl 9 Zipkit, pinch the tcl_library and tk_library directories coming for a *-tk Zipkit, sloppy of the Zikit's target platform:

Congratulations! You now person the ineffable knowledge utilized to beryllium shared only by those closest to the Tcl project. Now, we tin yet look astatine how Zipkits are utilized to build single-file binaries for Windows, Linux and macOS.
What About Tclkits?
For decades Tclkits were disposable arsenic self-contained executables that bundled the Tcl interpreter, the Tk toolkit, and a virtual filesystem (VFS) into 1 file, to beryllium utilized arsenic a portable runtime for moving Tcl/Tk scripts. A emblematic Tclkit uses the Metakit embedded database aliases a akin exertion to supply the VFS. When you tally a Tclkit, it mounts this soul database arsenic a filesystem (usually arsenic //zvfs/ aliases lib/tcl), allowing the book to entree bundled packages arsenic if they were connected a beingness disk. Tclkits served the Tcl developers well, but are now being superseded by the autochthonal zip-based features.
Here's wherever Tcl 9 Zipkits travel in. These are a caller type of a statically linked, portable tcl and wish interpreters that supply the commands to create single-file binaries by "stitching" a Zipkit to a zip archive pinch the exertion files. Upon startup, a Zipkit first looks "into itself", and if a information package is available, it mounts it into a virtual record strategy and looks for the "main.tcl" record to run.
If you're starting retired pinch Tcl 9, hide astir Tclkits, and attraction connected Zipkits arsenic devices for building single-file binaries.
Zipkits and Standalone Apps
To create a standalone executable you request a statically compiled Tcl aliases Tk expert — the aforementioned Zipkit. Available for download here. Check retired really galore platforms you tin target pinch your binaries:
- Windows for x86 and x86_64
- Darwin for x86_64 (Intel Macs)
- Darwin for arm64 (M-series Macs)
- Darwin cosmopolitan binary
- Linux for x86_64
- Linux for arm64
- Linux for RiscV 64-bit
- Solaris 11 / OpenIndiana
The truth that the Zipkit binary is virtually attached to the zip archive pinch the exertion files intends you tin build binaries for immoderate OS from immoderate OS by simply changing the "template/seed Zipkit" you usage to bundle with the zipfs mkimg command. Of course, if your scripts make usage of immoderate platform-specific extensions, for illustration twapi for Windows, you'll request to make judge to proviso them wrong of your zip archive and telephone the correct 1 successful your scripts based connected the level — .DLL files for Windows, .SO ones for Linux, and astir apt nary for Android, arsenic Androwish is already a "battery included" Tcl/Tk environment.
You tin moreover unfastened your packed zipkit successful an archiving app (i.e. 7zip) and research aliases moreover modify the contents, and past restart the app to spot the changes! Here's a emblematic files building of a zip attachment, pinch the files basal for the app to function:

Building binaries pinch zipkits is simply a comparatively caller and progressive area of development. Since I'm still moving connected my ain single-file binary build toolchain, I'll extremity here, and whitethorn return to this taxable later, successful a abstracted blog post.

If you are willing successful building your ain single-file Tcl/Tk apps, make judge to publication the docs to get the thought connected the wide attack connected assembling the contents of your //zipfs:/app/ VFS directory, which you'd past zip and stich to a Tcl aliases Tk Zipkit. One is for CLI apps, while the different is for GUI ones, naturally. So, for instance, for non-GUI apps, usage a *-tcl Zipkit and omit packing tk_library into the //zipfs:/ virtual record system.
Web-Based Apps pinch Tcl Wapp
"GUI-starved" Python developers are good acquainted pinch processing Web Apps utilizing Flask aliases Bottle, wherever Python acts a "headless" server that serves pages viewable successful immoderate browser. Tcl tin do the aforesaid utilizing the Wapp framework. Except dissimilar galore Python frameworks that require a abstracted accumulation server (like Gunicorn aliases uWSGI) to beryllium secure/stable, Wapp is designed to beryllium production-ready retired of the container pinch only utilizing Tcl’s robust soul socket handling aliases via CGI/SCGI.
What makes it worthy of looking into is the truth that D. Richard Hipp is the original writer and creator of Wapp. Yep, the very aforesaid guy, who's globally celebrated for creating SQLite, the astir deployed database motor successful the world. In fact, the illustration checklist application, successful the shape of a Tcl script, is the same, arsenic the 1 utilized to negociate the testing and the merchandise routines for SQLite. Source codification for the checklist exertion tin beryllium recovered astatine https://sqlite.org/checklistapp.
The Fossil Connection
Hipp built Wapp because he needed a simple, secure, and lightweight measurement to build web interfaces for his different projects, for illustration the Fossil Distributed Version Control System, which hosts the Tcl/Tk and SQLite root codebases. Indeed, while astir group only spot Tcl and Tk root codification connected GitHub, those are really mirrors.
The "Source of Truth" for Tcl/Tk improvement is hosted at:
- https://core.tcl-lang.org/tcl — Tcl Core
- https://core.tcl-lang.org/tk — Tk Core
When you sojourn those links, you'll spot the Fossil Web UI (powered by Wapp/SQLite) moving successful production. The Tcl halfway developers chose Fossil specifically because it aligns pinch their "Cathedral" improvement model: a small, highly trusted halfway squad maintaining a high-quality codebase of a robust framework.
Fossil is simply a fascinating task successful its ain right. Do cheque it retired if you're looking for a single-file, complete, portable VCS for your projects. Here's short history of really and why it came to be.
For now, let's return to Wapp.
Wapp.tcl
Key features of Wapp:
- Wapp is simply a single-file framework. Just arsenic SQLite is simply a single-file database, Wapp intends to beryllium a azygous "wapp.tcl" record that provides the basal functionality expected from a web app. So a complete app is simply a azygous record of Tcl
- If needed, the web interface tin beryllium made accessible from anyplace connected the network, which is 1 of the main benefits of this approach, compared to the purely section CLI/GUI deployment
- Because Hipp deals pinch captious infrastructure, Wapp is designed to beryllium "secure by default." It handles things for illustration URI decoding and parameter sanitization automatically to forestall communal web vulnerabilities
- Wapp is meant to beryllium resistant to attacks and exploits and is built to activity reliably for decades without requiring a monolithic stack of dependencies, successful fact…
- …while Wapp can tally arsenic a standalone script, it is often utilized down a "real" web server (like Apache aliases Nginx) utilizing the SCGI protocol. Wapp handles this translator seamlessly
You, too, tin deploy a web app pinch Tcl, arsenic mini arsenic a one-file, cross-platform Tcl script. Download the latest wapp.tcl, past see it successful your scripts either pinch the root command, aliases by turning it into a civilization .tm module to see pinch package require. Done! You've sewage a reliable and unafraid Web Framework that includes a built-in Application Server for cases erstwhile you require a web-based interface for your Tcl apps.
Combine Wapp pinch a lightweight JavaScript html hold room for illustration htmx, adhd a mates of lines of css, and you've sewage yourself an performant, secure, low-overhead modern Hypermedia Driven Application, while penning conscionable a fistful of Tcl procedures and 0 lines of JavaScript.
This is simply a very businesslike attack to building a Tcl Web App interface.

BTW, if you deliberation this is the only "correct" attack to making modern apps pinch interfaces — deliberation again. A web browser is simply a heavy sandboxed environment. If you walk excessively overmuch clip polishing web apps for the web, you mightiness miss the powerfulness of Tcl/Tk’s native GUI capabilities for illustration real-time asynchronous feedback. Use Wapp if the "web-based" request is simply a difficult necessity for distant access, aliases erstwhile making the UIs to beryllium easy accessible from mobile devices. If you conscionable request a UI for a local tool, don't overcomplicate and "overly-abstract" things, and enactment pinch Tk. After all, it is besides cross-platform by design.
More Cool Facts About Tcl/Tk
To information retired my Tcl/Tk overview, present are a fewer notable facts I’ve gathered on the way. Listed successful nary peculiar order.
"WebAssembly From a Parallel Universe"
The tech manufacture presently views WebAssembly (Wasm) arsenic the eventual solution for moving untrusted codification astatine near-native speeds wrong a unafraid sandbox. Whether successful a browser aliases a server-side WASI environment, Wasm’s superior worth proposition is its isolation.
Believe it aliases not, but Tcl developers person had entree to robust, high-level sandboxing since the early 90s. While Wasm isolates astatine the instruction level, Tcl isolates astatine the logical level done "Safe Interpreters."
To unafraid a book successful Tcl each you request to is spawn a kid expert and limit its capabilities. By default, a "safe" expert is calved moreover without the expertise to touch the record system, unfastened web sockets, aliases load outer libraries. You tin past widen these permissions, and hide aliases expose commands arsenic you spot fit.
# Create a restricted environment interp create -safe mySandbox # Explicitly span only the functionality you want to allow # Here, we let the sandbox to play audio via a big function interp othername mySandbox playAudio {} MyHostAudioFunction # The sandbox tin do mathematics and logic, but it can't delete files connected diskSafe interpreters are immensely useful to not conscionable tally untrusted code. They unfastened up a anticipation to connection highly curated Tcl personification sessions pinch entree to no autochthonal Tcl commands, but those which you yourself created for your users, and specified conception is known as:
Radical Language Modifications With DSL
Tcl is known to beryllium a awesome prime for the improvement of Domain Specific Languages (DSLs).
Technically, Tcl is not truthful overmuch a "language" successful the accepted sense, but a command-processing engine. As you now know, Tcl loops, adaptable assignments, and conditional logic are each commands followed by arguments. So you tin create full ensembles of commands your peculiar business needs, and tally those wrong a Tcl's safe interpreter, yet ending up pinch a wholly customized Tcl interactive session.
To quote Salvatore antirez Sanfilippo from his "Tcl the Misunderstood":
If you specify a process called unknown it is called pinch a Tcl database representing arguments of each bid Tcl tried to execute, but grounded because the bid sanction was not defined. You tin do what you for illustration pinch it, and return a value, aliases raise an error. If you conscionable return a value, the bid will look to activity moreover if chartless to Tcl, and the return worth returned by unknown will beryllium utilized arsenic return worth of the not defined command. Add this to uplevel and upvar, and the connection itself that's almost syntax free, and what you get is an awesome situation for Domain Specific Languages development. Tcl has almost nary syntax, for illustration Lisp and FORTH, but location are different ways to person nary syntax. Tcl looks for illustration a configuration record by default:
disable ssl validUsers jim barbara carmelo hostname foobar { let from 2:00 to 8:00 }The supra is a valid Tcl program, erstwhile you specify the commands used, disable, validUsers and hostname.
Let's return Salvatore's illustration and re-implement it successful JS, assuming likewise named functions had been defined arsenic well:
disable("ssl"); validUsers("jim", "barbara", "carmelo"); hostname("foobar", () => { allow("from", "2:00", "to", "8:00"); });Doesn't rather look arsenic elegant, does it?
To get person to the "feel" of Tcl, modern JS libraries (like Express aliases Knex) usage method chaining. It’s cleaner than the erstwhile version, but still visually noisier than Tcl:
// A emblematic JS Fluent API approach config .disable("ssl") .validUsers(["jim", "barbara", "carmelo"]) .hostname("foobar", (host) => { host.allow().from("2:00").to("8:00"); });This is why Tcl was historically the connection of prime for banking guidance systems and the EDA industry. In-house developers could build civilization DSLs for labor and moving their codification wrong Safe Interpreters. These "sandboxed" environments restrict execution to a specific, tailored group of commands.
To the end-user, it feels for illustration they are interacting pinch the strategy utilizing "natural language". In reality, they are utilizing a custom-built DSL, powered by Tcl's elastic pre-processors and command-tracing capabilities:
# Command statement argument... checkout 1000 USD measure existent [board_id psu]Finally, if you were consenting to return it to different level, you could constitute your ain Macro-Assemblers.
For example, here's 1 I wrote to instrumentality "chained" multi-stage information processing, akin to the aforementioned JavaScript method chaining. It takes successful a group of literal "instruction blocks" which are past utilized to make a Tcl-native bid to post-process the provided drawstring successful stages:
# Load successful a civilization package package require Batcher # Prepare a civilization Macro-Engine parser Batcher batch string_processor { {s:trim} {s:reverse} {s:range 10 30} {s:repeat 2} {s:replace 0 11 "Replacement "} } set my_str " !elbadaer won s'tI - AAAAAAAAAA " puts "Processed String: [string_processor $my_str]" # RESULT: 'Processed String: Replacement readable! - It's now readable!'It's for illustration having a room of Lisp macros wrong Tcl, code that writes code.
With DSLs developers tin beautiful overmuch alteration the travel of programming pinch a homoiconic connection for illustration Tcl, to make it suit their preferred coding style, to an extent.
Why Is Doing Math successful Tcl So Awkward?
I'm reasonably definite erstwhile you commencement learning Tcl you'll get irritated by the truth that expr must beryllium utilized to measure immoderate mathematics expression. Then you will study that the expressions themselves must to beryllium placed wrong the curly braces {} to forestall the Tcl expert from substituting the variables before they scope the expr command. Which will lead to a realization that expr acts arsenic its ain "mini-compiler" connected the full {expression} nary less. It looks wrong the braced block, finds the dollar signs and the nested bid blocks, and past performs adaptable lookups and mathematics in 1 optimized step, yet providing a bully mentation for why mathematics is done that way.
Because of this, you person free reign and tin build up expressions of immoderate complexity, arsenic agelong arsenic expr knows really to construe the operators and functions inside, for illustration so:
set downpayment [expr { round(max(0, min($downpayment, $principal * 0.95))) }]
Tcl's bytecode compiler sees expr { ... } and pre-compiles it into highly businesslike instrumentality instructions. When the look parser encounters a mathematical usability specified arsenic sin($x), it replaces it pinch a telephone to an mean Tcl bid successful the tcl::mathfunc namespace.
The expr bid moreover allows splitting expressions into lines and supports comments:
set downpayment [expr { round( # Round to integer max( # Ensure it isn't negative 0, min($downpayment, $principal * 0.95) ) ) }]As for the "dreaded braces" — without them, Tcl expert converts everything to a drawstring first, past expr converts it backmost to a number, which wastes CPU cycles. With braces {}, the soul Tcl_Obj stays a numeric type, but astir importantly — bracing prevents "double substitution." If a adaptable contained thing malicious for illustration [exec rm -rf /], a braced expr would dainty it arsenic a string/error, whereas an unbraced 1 mightiness really execute it. Think of it arsenic an "SQL Injection". A much elaborate illustration tin beryllium recovered below, in the conception connected Security.
Finally, expr is smart capable to support "lazy evaluation". Operands are not evaluated if they are not needed to find the outcome. For example, expr {$v?[a]:[b]} will measure either [a] aliases [b], depending connected the worth of $v.
To summarize, mathematics has to beryllium done pinch expr because of Tcl's whitespace-separated bid syntax. And whenever you usage expr, remember to ever brace your expressions.
Tcl 9.0 — Strict, Honest and Reliable
I antecedently mentioned that Tcl 9.0 is amazingly honorable and strict for an interpreted language. Consider the pursuing scenario: you person a matter archive aliases a Tcl book saved arsenic UTF-8 pinch a BOM (Byte Order Mark). BOM is simply a "magic number" astatine the very opening of a matter file, which was designed to lick respective problems that arose erstwhile the world moved from ASCII to Unicode. Nowadays, unless you request to activity pinch really aged software, there's usually nary request to encode UTF files pinch BOM. But what matters correct now is that the BOM itself is technically a Zero-Width No-Break Space — U+FEFF.
Here is really different languages grip reference that record into a variable:
- C#, JavaScript aliases Python spot those bytes, recognize they are a metadata signature, and discard them earlier the programmer sees the string. The connection "lies" astir what's really successful the record to make programmer's life much convenient
- Tcl 9 treats the record arsenic a watercourse of information "as is", without lying to you, to debar changing the contents successful an unexpected way. If the first characteristic is simply a U+FEFF, Tcl assumes that if it exists successful your file, you intended for it to beryllium there. It refuses to curate your information for you. If you past effort to append thing to the opening of the contents of the adaptable (even conscionable a azygous space), it will beryllium added before the U+FEFF symbol. If you past prevention this record and effort executing it arsenic a Tcl script, Tcl will return an error, because it will skip the space, arsenic it does pinch each whitespace normally, stumble upon the BOM awesome and effort to look it up arsenic a command. And since bid pinch specified a sanction doesn't exist, the programme will crash. Here's what specified an "appended" record looked for illustration successful my IDE:

And this is what happened upon trying to tally this code:
invalid bid sanction " #" while executing " # -----------------------------------------------------------------------------" (file "main.tcl" statement 43)This is the consequence of the "Tcl Improvement Proposals": TIP 601, TIP 656 and TIP 657 starring to changes connected really Tcl handles encodings. Starting pinch Tcl 9.0, the expert ensures that the U+FEFF characteristic (or immoderate others) is preserved exactly arsenic it exists successful the byte stream. Which I wholeheartedly support.
In Tcl 8.6, the I/O strategy was "lossy" by default. If it deed a byte series it didn't understand (or a series that didn't perfectly align pinch the expected encoding), it would often usage a fallback (like ISO-8859-1 mapping). Or simply disregard the correction to support the programme running, perchance starring to unexpected behaviour which would beryllium difficult to diagnose.
Therefore, to decently reside moving pinch UTF-8 BOM encoded files, pinch Tcl 9.0 you request to explicitly portion this useless awesome from the stream:
# Read record contents into a variable set fh [open $path_to_script r] # IMPORTANT! Configure the transmission to grip UTF-8 # Using 'strict' ensures the record is valid UTF-8 (which is the default for Tcl 9), # and will raise an correction if bad bytes are recovered successful the stream # 'replace' action should only beryllium utilized for reference of messy logs and such fconfigure $fh -encoding utf-8 -profile strict set script_content [read $fh] close $fh # Strip the UTF-8 BOM characteristic (\uFEFF) if it exists! set script_content [string trimleft $script_content \ufeff] # Ensure location is simply a clear separation betwixt the joined parts and the script set final_script [string feline $appendix "\n" $script_content]This strictness is what makes Tcl truthful reliable arsenic a cross-platform and future-proof instrumentality framework. It will do exactly what you're asking it to, eliminating the dreaded "it useful connected my machine" bugs, and ensuring your package is built connected mechanical certainty alternatively than a compiler's "best guess".
I for illustration it!
Platform-Specific Libraries
Tcl/Tk scripts aren't restricted to only cross-platform commands. You tin widen their functionality by penning autochthonal libraries aliases leveraging existing Tcl/Tk packages.
For example, if you're penning scripts for Windows, the twapi extension is your champion friend:
The hold provides entree to the Windows API astatine 2 levels. A nonstop interface to the supported Windows API is provided wherever the Tcl commands straight representation to Windows functions arsenic described successful Microsoft Windows SDK. The recommended interface is simply a higher level interface that is much convenient, powerful and overmuch easier to usage than the earthy Windows API.
In operation pinch the built-in accommodation successful Tcl, TWAPI makes it imaginable to constitute a wide assortment of Windows applications ranging from desktop applications to web servers moving arsenic Windows services
In my case, 1 time I needed to show the changes of definite SMART readings for 1 of my drives. These are disposable arsenic hexadecimal values successful the CrystalDiskInfo interface, truthful they needed to beryllium converted to decimal each time. But I besides couldn't person CrystalDiskInfo moving non-stop to make judge its monitoring didn't interfere pinch the target drive's behavior.

Instead of having to manually reopen the calculator and past the app each clip I needed to cheque the values, I quickly wrote a Tk GUI book that'd fto maine person betwixt hex and decimal values, would clasp the antecedently entered worth successful its input section and could commencement CrystalDiskInfo pinch a property of a button. The second was not a elemental "exec" because CDI needs to beryllium tally pinch administrator privileges. Here's wherever the twapi room came successful handy, arsenic it tin "talk" to Windows straight and inquire it to tally a definite app pinch the required permissions:
# Run CrystalDiskInfo located adjacent to the script package require twapi proc start_cdi {} { group exe [file normalize [auto_execok CrystalDiskInfoPortable.exe]] # 'runas' is the verb that triggers the UAC elevation prompt if {[catch {twapi::shell_execute -path $exe -verb runas} err]} { puts "Elevation grounded aliases cancelled: $err" } } # ...code-code-code... ttk::button .f.btnStart -text "Start CrystalDiskInfo" -command start_cdiOr really astir the pursuing "production pipeline tool" scenario:
I ain a perpetual licence for Adobe Creative Suite 6 Production Premium. Yes, the CS6 one, released successful 2012. It covers each of my media accumulation needs for video, audio and image accumulation and manipulation, allowing maine to dodge Adobe's Creative Cloud subscription trap. The problem is that the past type of the Adobe Camera RAW plug-in compatible pinch the CS6 suite is type 9.1.1 from 2015. It's a perfectly functional and highly tin instrumentality for moving pinch RAW images, but naturally, it doesn't support RAW files coming from cameras released aft 2015.
Luckily, Adobe offers a very useful freeware tool: Adobe Digital Negative Converter, which "enables to easy person camera-specific earthy files from supported cameras to a much cosmopolitan DNG earthy file. Another use of utilizing the DNG Converter is backward compatibility". It gets regular updates and tin export DNG files compatible pinch peculiar awesome versions of Adobe Camera Raw, including 9.1.1 aliases older.
It would beryllium cleanable if not for an unfortunate omission — there's nary action to sphere the original record creation aliases modification dates erstwhile exporting DNGs! Neither the GUI nor the command-line parameters connection specified an option. Of course, the DNGs themselves clasp soul metadata including dates and camera info, but it's still very inconvenient to suffer the filesystem creation day and clip aft the conversion, replacing them pinch the timestamp of the conversion procedure. A azygous sprout could past for hours aliases moreover days, and being capable to shape the files based connected the their filesystem timestamps is conscionable excessively valuable.
Tcl/Tk + twapi to the rescue! By referencing the charismatic command-line documentation for the DNG Converter, I was capable to build a elemental instrumentality which:
- Allows processing a files of RAW images utilizing immoderate number of user-specified command-line parameters
- Uses twapi::get_file_times and twapi::set_file_times commands to publication the creation/modification timestamps of the original RAW files and re-apply them to the recently exported DNG files

As a consequence — I tin bulk-process RAW files from immoderate caller integer camera to get DNG files compatible pinch the CS6 suite of apps, clasp filesystem timestamps, and get files that are smaller successful size acknowledgment to very businesslike DNG compression. The twapi package is required because axenic Tcl doesn't connection the functionality to alteration the record creation timestamp, only providing commands to publication aliases constitute record modification and last access timestamps via file atime and file mtime.
There are galore much Windows API wrapper commands available, truthful you could create apps that would rival AutoHotkey scripts successful their expertise to interact pinch the operating system, seizure hotkeys and telephone useful Windows functions directly.
On Nullable Types
Tony Hoare introduced Null references successful ALGOL W backmost successful 1965 "simply because it was truthful easy to implement". He calls it a "Billion Dollar Mistake" because of the decades of strategy crashes it caused.
In C++, a null is simply a literal zero successful a pointer register. In Tcl and JavaScript, for example, "null" is conscionable different state wrong a overmuch larger, much analyzable "Object" aliases "Command" structure.
And yet JavaScript someway allows to cheque if a definite adaptable is null. How? It's a "charade." An undefined aliases null is really a unsocial representation changeless that the JS Engine’s "Command Router" knows really to handle. It’s a circumstantial "Data type that represents nothingness." With this, JavaScript tries to emulate the "null" acquisition of C++ but ends up pinch a confusing null vs undefined mess. Those of america pinch a web improvement inheritance cognize this excessively well. Strictly speaking, null was intended to correspond "the intentional absence of an object," while undefined represents "the absence of a value. Did you cognize that?
At the aforesaid time, successful Tcl the "nullability" you spot successful different languages is handled by existence. You don't cheque if a adaptable is null since it makes nary sense, arsenic it. You cheque if it exists successful the namespace: info exists varName, aliases if its drawstring magnitude is zero.
Tcl doesn't dress to person nulls, and alternatively only has states connected Storage (Existence) and Content (The String):
- Existence: info exists varName — is is moreover there?
- Content: if {$varName eq ""} ... — OK, but is it empty?
In Tcl, if you want a "null," you usage an quiet drawstring "". Because Tcl automates the conversion betwixt strings and different types (integers, lists), an quiet drawstring is the only "logical null." If you effort to do mathematics connected an quiet string, Tcl doesn't springiness you 0 aliases NaN and alternatively throws an correction because an quiet drawstring is not a valid operand.
In a way, Tcl avoids the "null pointer exception" by simply refusing to admit that a pointer tin beryllium a worth successful the first place, and forces you to beryllium much definitive astir your data's existence.
Limitations (and "Quirks")
Tcl is not perfect. Its bid nature, and the simplicity of its syntax displacement the load of correctness from the compiler to the developer’s discipline. In languages for illustration JavaScript aliases PHP, the favoritism betwixt data and code is enforced by the interpreter. In Tcl, because everything is simply a string, you are the interpreter.
For instance, if you aren't observant pinch grouping aliases don't understand the differences betwixt the grouping types (braces {} vs. quotes ""), you tin extremity up pinch double substitutions that lead to information vulnerabilities aliases bugs that are difficult to trace successful a ample tool.
Trying to do profoundly nested bid calls wrong different bid calls will quickly make the codification unreadable. This should make programmers recognize that breaking codification into smaller blocks and opting for a line-by-line and step-by-step execution is simply a amended approach. But it doesn't guarantee that each devs would do this, because Tcl doesn't enforce immoderate peculiar coding style and restrictions connected you, for illustration Python does for example.
Because Tcl provides the devices to redefine the connection itself, it incentivizes a level of "cleverness" that is seldom seen successful much rigid environments. What originates arsenic a small, elegant book tin quickly mutate into an unmaintainable maze of civilization commands and vulnerable substitution chains, simply because the programmer wanted to "look cool". Without the guardrails of enforced style aliases strict typing, the "quirks" of the connection tin move from being insignificant annoyances to becoming structural liabilities. To understand why Tcl tin consciousness for illustration a minefield, 1 must look intimately astatine the circumstantial mechanical behaviors wherever this "simplicity" breaks down successful practice, and needs self-imposed guardrails aliases "wrapper" commands.
The Tcl "Quoting Hell"
It's existent that the magnitude of quotes, braces and brackets you'll brushwood successful Tcl is higher than successful C-like languages. And the problem isn't pinch Tcl itself, but alternatively pinch the coding style that begets it.
Let's look astatine a communal script successful GUI aliases CLI development: fetching a worth from a nested dictionary, performing a mathematics operation, and formatting the consequence for a explanation successful the interface.
In a azygous line, Tcl allows you to nest commands indefinitely. While powerful, this requires you to parse the execution bid from the wrong out, manually search each substitution level.
# Good luck trying to understand this astatine a glance set show "Total: [format "%.2f" [expr {[dict get $invoice items electronics price] * 0.85}]]"This is dangerously adjacent to becoming unreadable. Add a mates much nested levels and it's a disaster.
That's why you should for illustration breaking your codification up into chunks and steps, for amended readability and maintainability successful the future. Compare the erstwhile one-liner to this version:
# The comments aren't moreover needed! The codification is 'self-documenting' set rawPrice [dict get $invoice items electronics price] set discountedPrice [expr {$rawPrice * 0.85}]; # Braced for safety/speed, arsenic always! set formattedPrice [format "%.2f" $discountedPrice] set displayString "Total: $formattedPrice"Suddenly, Tcl doesn't look for illustration specified a "quoting hell", does it?
Both approaches are so functional. But since successful Tcl the "correct" measurement to constitute codification isn't enforced by the language, it's a prime you person to make each clip you deed the Enter key. If alternatively of trying to constitute readable codification you take to "save vertical space" by packing respective different algorithmic steps into "clever" one-liners, you'll extremity up building riddles alternatively of code, and we'll ne'er go friends.
There are besides immoderate very important specifications often missed by beginners. For instance, they're told that thing wrong curly braces {} is treated arsenic drawstring literals, and truthful doesn't spell though immoderate information of adaptable substitution… But braces {} placed wrong quotes "" are treated for illustration immoderate different character. So this may, and will, confuse you arsenic a beginner, until you get a patient grasp astatine really quoting and substitution rules activity successful Tcl:
# String literals - nary substitutions wrong curly braces set str {puts "[clock seconds]"} ; # => puts "[clock seconds]" # BUT wrong quotes curly braces are treated arsenic normal characters # Double quotes person a higher "precedence" successful the parser's eyes, # truthful they show Tcl to commencement substituting immediately, rendering # the curly braces wrong arsenic thing much than ornamental text. set quoted "puts {[clock seconds]}" ; # => puts {1773469272}This, coupled pinch the truth that information successful Tcl tin beryllium represented pinch strings, people leads to specified "Tcl'isms" arsenic having to [list] callbacks to person Tcl automatically flight spaces successful strings. Or having to usage the description usability {*} moreover connected a seemingly cleanable string, to explicitly show Tcl that it should construe that drawstring as a database of words, to beryllium capable to usage it arsenic a bid pinch arguments, and not conscionable a non-existent azygous bid pinch whitespaces and typical characters successful its name:
set fullcmd {puts "Unpack me!"} # This fails $fullcmd; # => Error: invalid bid sanction "puts "Unpack me!"" # This succeeds {*}$fullcmd; # => Unpack me!Realistically though, if you don't effort to do overly "clever" things pinch your code, you'll beryllium conscionable fine.
No Garbage Collection
Tcl has nary tracing garbage collector (like the ones successful Java, Python, aliases C#). Instead, Tcl uses Reference Counting to negociate memory. While some RC and GC are forms of "automatic representation management," they behave very otherwise nether the hood.
It's not a problem astatine each if you don't usage the TclOO halfway extension, though I'd reason you astir apt should. Regardless, whenever you create TclOO "object instances", you are really creating "stateful objects" which are, arsenic we recovered retired previously, fundamentally sophisticated bid dispatchers, and not "objects" successful accepted sense.
In JavaScript, Python aliases C# you tin conscionable fto the entity autumn retired of scope and it will get automatically garbage collected. In TclOO, an "object" is simply a bid successful a namespace. Commands don't "fall retired of scope" conscionable because a process ends. So if you create an ::oo "object" wrong a loop/procedure and don't explicitly telephone destruct aliases usage a adaptable trace to termination it, you will leak memory!
This is manageable, of course, if you dainty your TclOO-created "objects" for illustration C++ pointers: you brought them into this world, truthful you must return them retired erstwhile the clip comes. In a production, mission-critical environment, aliases erstwhile moving successful a team, you'd usage a try…finally artifact to do this:
proc OneShotProc {} { # Create the TclOO 'object' (the command) *outside* the effort block. # This way, if the constructor fails, TclOO cleans itself up natively. group tempInstance [SomeClass new] # Execute each logic wrong the 'try' block effort { # ... Complex codification pinch aggregate returns etc ... } yet { # This is guaranteed to tally and delete the command $tempInstance destroy } }This is the modular attack for cases wherever definitive readability is required: anyone reference the codification will instantly spot the bound of the assets lifecycle.
However, erstwhile coding your own tools, you're free to absurd specified inconveniences distant entirely. For example...
managed_create
I utilized Tcl's very ain functionality of adaptable traces, and will stock a wrapper to create an entity successful a "managed way". I telephone it managed_create. Here's really it goes and really to usage it:
# Creates an entity and binds its life to a adaptable successful the caller's scope # USAGE: # In each of these cases *desctructor is called* whenever the 'handle' adaptable is *changed* # managed_create MyClass mc args ; # create an 'instance' pinch 'mc' arsenic a 'handle' # group mc null ; # tin virtually group the grip adaptable worth to 'null' for lulz # unset mc ; # aliases amended yet - unset it, 'C#-like', it will get cleaned up # Compatible pinch mill commands arsenic well # managed_create twapi::comobj petition "WinHttp.WinHttpRequest.5.1" proc managed_create {className varName args} { # Construct the afloat qualified path if {[llength $className] > 1} { group className [join $className "::"] } if {![string lucifer "::*" $className]} { group className "::$className" } # Check if it's a mill bid (like twapi::comobj) aliases a TclOO people that needs 'new' if {[info commands $className] ne "" && ![info entity isa people $className]} { group obj [{*}$className {*}$args]; # It's a mill bid (TWAPI style) } other { group obj [$className caller {*}$args]; # Assume it's a TclOO class } # Link 'handle' to the adaptable sanction provided by the caller upvar 1 $varName handle group grip $obj # Define the lambda assemblage separately truthful the trace tin mention to it group lambdaBody { {o assemblage n1 n2 op} { # Link to the adaptable being traced successful the caller's scope upvar 1 $n1 v # Remove only THIS trace by reconstructing the nonstop bid prefix utilized to adhd it trace region adaptable v {write unset} [list use $body $o $body] # Use 'info commands' to validate the entity beingness wrong the trace if {[llength [info commands $o]]} { drawback { $o destroy # puts "Memory leak prevented! Object destroyed and grip trace removed." } } } } # Add the trace by passing the lambda assemblage arsenic the 2nd statement ($body) trace adhd adaptable grip {write unset} [list use $lambdaBody $obj $lambdaBody] return $obj }This is your aureate summons into effortless, modern Tcl/Tk OOP. Use it to create "instances" of your ain TclOO classes and COM/twapi objects, to ne'er interest astir representation leaks erstwhile the adaptable (or a "handle/pointer") that references the bid gets changed aliases goes retired of scope. The referenced bid will ever get destroyed.
And since this is efficaciously for illustration applying C-pointer subject to Tcl, erstwhile you walk specified a "pointer" to different functions, treat it arsenic such! Remember that the trace is attached to the variable name, not the drawstring value. So make judge to upvar 1 to the passed "handle" variable, to debar copying it. This measurement you'll make judge that the original traces occurrence arsenic soon arsenic anyone accidentally messes pinch this "handle" (or a "pointer") from wrong immoderate different scope aliases a procedure.
Note connected Tk widgets: In Tcl/Tk, creating a widget for illustration ttk::button .mybtn creates a command. Just for illustration TclOO "objects", widgets do not dice erstwhile the proc ends. If you create a popup model aliases a framework wrong a procedure, it will unrecorded everlastingly until you manually destruct the .path to the widget. Thankfully, you don't request to widen managed_create to support Tk widgets! — Destroying immoderate Tk widget besides destroys each of its kid widgets. Therefore, whenever possible, debar creating widgets straight connected the guidelines — .mybtn aliases .mylbl — and alternatively genitor them nether a instrumentality widget — framework aliases canvas, for illustration so: ttk::button .myfrm.mybtn. Then, whenever you request to cleanable up a full character of widgets parented nether that container, simply telephone destroy .myfrm.
managed_channel
Of course, channels besides request to beryllium decently closed! And it's conscionable the aforesaid arsenic pinch TclOO "objects" — if you usage unfastened wrong a procedure, but hide to adjacent it earlier the process ends, you'll extremity up pinch a permanently unfastened channel. Not good.
Thankfully, the aforesaid adaptable trace attack tin beryllium applied to channels.
proc managed_channel {varName openArgs} { # Link to the caller's variable upvar 1 $varName handle # Open the transmission (e.g., unfastened "test.txt" r) group chan [open {*}$openArgs]; # Use {*} to grow the openArgs list group grip $chan # Arm the trace. If the grip is overwritten aliases the proc ends, adjacent the channel trace adhd adaptable grip {write unset} [list use {{c n1 n2 op} { if {$c successful [chan names]} { adjacent $c # puts "Channel $c closed automatically via $op." } }} $chan] return $chan }There. These wrappers efficaciously backport modern scope-based assets guidance into Tcl and should screen 90% of each imaginable representation leaks, allowing you to attraction connected logic alternatively than janitorial work.
No Centralized Package Manager
Tcl lacks a modern, ubiquitous package head for illustration npm aliases pip. While teacup existed for ActiveTcl, it’s mostly legacy. This intends that you either person to spell hunting for packages aliases extensions, to download them compiled, aliases arsenic root codification to compile yourself. Or simply trust connected what's commonly called a Tcl/Tk "Batteries Included" Distribution. These are third-party binary distributions, which see each sorts of battle- and time-tested packages retired of the box, and truthful let a one-time installation to screen the immense mostly of Tcl/Tk use-cases. In a way, this mirrors the C#/.NET acquisition wherever you instal the SDK and "everything conscionable works" retired of the box, isolated from without nuget to propulsion caller packages for you connected request.
Here are immoderate of the amended known, actively-maintained BI distributions:
- Magicsplat — distribution developed and maintained by Ashok Nadkarni, the very writer of the book "The Tcl Programming Language: A Comprehensive Guide". The distro tin beryllium installed per personification aliases system-wise, pinch the automatic strategy way and filetype-association configuration successful the second case, which is simply a very convenient, Windows-first approach. It comes pinch little other packages compared to the BI BAWT distro, but contains astir commonly utilized and tested packages, while excluding those which make small consciousness for a modern Tcl/Tk 9 distribution. This is the distro I usage connected my Windows machines and tin highly urge it, particularly if you're conscionable starting retired pinch Tcl/Tk
- BAWT — configurable model by Paul Obermeier that automates creating civilization BI distributions. Certain precompiled installers are besides available, primarily for Windows, It includes almost thing that tin beryllium compiled for Tcl 9, moreover if it hasn't been afloat stress-tested. It’s a "maximalist's" Tcl/Tk distribution, aimed astatine seasoned developers who cognize precisely what they're doing and why they mightiness request mostly outdated packages for illustration tix. But the existent worth of BAWT is successful the truth that Mr. Obermeier provides each the devices basal to build a functional, portable Tcl/Tk distribution for respective platforms and CPU architectures. It's precisely really I was capable to get Tcl/Tk 9.0.3 moving connected a mini Arm-based SBC — by compiling Tcl/Tk and each the packages I wanted to include, straight for the Arm64 platform
- ActiveState — listed present conscionable for posterity. This was erstwhile the undisputed king of Tcl distributions. Apparently, if you were a Tcl developer betwixt 2000 and 2015, ActiveTcl was your default choice. But past astatine immoderate constituent ActiveState realized that large banks and security companies person millions of lines of existing Tcl 8.4/8.5 codification that they couldn't easy migrate from, and needed thief successful maintaining each of that infrastructure. This signaled a displacement successful ActiveState's business strategy, and led to the decease of the legendary teapot package manager, which ActiveState maintained betwixt years 2000 and ~2020. What this intends for the remainder of america is — ignore ActiveState. They're a shade of the past, focused connected their ain business, and nary longer of immoderate worth to a random developer who conscionable wants a versatile Tcl/Tk BI package aliases an installer
To get an thought conscionable really galore robust, battle-tested packages you get pinch a communal "batteries included" distribution. Try moving this successful the tclsh console:
catch {package require force_tcl_to_enumerate_all_packages} foreach sanction [lsort -dictionary [package names]] {puts $name}Here's what comes bundled pinch the "Magicsplat" Tcl 9 Windows distribution:

Do You Need a Package Manager?
While the truth that Tcl lacks a centralized package head whitethorn look for illustration a immense downside for a "modern" developer, it besides arguably improves Tcl/Tk's embeddability and, successful a way, — security.
A Tcl/Tk "Batteries Included" distribution tin beryllium arsenic mini arsenic conscionable 35MB successful lawsuit of Magicsplat (or a BAWT build pinch a akin composition). One specified installer contains the astir important achievements of complete 38 years of the Tcl/Tk improvement ecosystem. The immense mostly of the packages you'll find successful a BI distribution person been heavy time- and battle-tested, truthful you tin confidently usage them for mission-critical tasks. This, arsenic you tin imagine, besides mostly trivializes the deployment of Tcl/Tk connected systems: pinch conscionable 1 installer you tin replicate your full dev situation and beryllium judge that each azygous book will usability precisely arsenic expected.
I can't accent enough, conscionable really overmuch Tcl/Tk has changed my attack to "compute management", arsenic I've simply deployed it to each of my machines and gained a super-power to tally the aforesaid GUI Tk book connected immoderate of my machines, without the request to recompile for each aliases having to create strictly on the target level itself. It's thing you cannot do pinch immoderate of the existing interpreted languages. Python could beryllium an exception, since Tk GUI codification tin beryllium embedded into Python scripts and tally pinch tkinter, but it comes pinch a barebones, outdated type of Tcl and lacks astir of the essential, powerful Tcl packages. Packages which travel modular pinch a BI Tcl/Tk distribution: 100% fresh to usage and natively compiled for a level you deploy Tcl/Tk to.
As for security, I could effort and leverage the truth that you person to manually look for packages and often moreover compile them yourself, which makes it harder for you to go a unfortunate of a proviso concatenation attack. I could besides mention that pinch Python each you request to do is pip instal --upgrade some-package to download an updated, perchance compromised codification bundle to your machine, including each of the tens of limitations it besides needs you to person updated, and the risks that originate from that.
But I won't.
Arguing that "Tcl/Tk is much unafraid because it has nary package manager" would beryllium dishonest. Do I personally cognize Mr. Nadkarni, aliases Mr. Obermeier? No. But I do cognize that some of them are cardinal figures successful the Tcl dev circles, and to nefariously discuss a package aliases 2 would do tremendous harm to their credibility. So I'd alternatively spot 1 of these guys, than a 100 random devs connected the internet, who astatine immoderate constituent mightiness determine to abruptly operation their governmental views into their codebases, and origin harm — thing I antecedently covered connected the blog: The "node-ipc" Node.js package controversy.
But moreover if you're highly paranoid, it's overmuch easier to download the complete codebase of the BAWT framework, provender it into a codification researcher and cheque for immoderate signs of vulnerabilities aliases backdoors once, compared to having to do the aforesaid each time you update each of your Python aliases Node.js dependencies. Which is thing you really should do, because everything is backdoored by default.
If you don't spot a package that covers your needs successful 1 of the BI distributions, you tin find tons of extensions astatine core.tcl-lang.org.
Mediocre IDE Support
Indeed, to my knowledge, isolated from the highly-specialized devices for the chip-making manufacture (which I person nary entree to), location is only a fistful of IDEs that connection support for "generic Tcl", and astir of them only do truthful acknowledgment to the community-made extensions. Due to Tcl being truthful pliable, arsenic good arsenic its space-delimited syntax wherever commands and arguments travel each different successful a statement almost free-form, it's challenging to connection a blase autocomplete solution for this language.
For example, the only extensions I was capable to find for VSCode (all 2 of them) mostly connection basal codification highlighting, immoderate codification snippets and supply wide archive outlining capabilities. See the IDEs section beneath for much details. All successful all, Tcl IDE support is… adequate.

In contrast, this is the area wherever C-like aliases strictly-typed languages radiance simply owed to their "syntactic rigidity". When it comes to the homoiconic Tcl, codification tin beryllium some logic and information astatine immoderate time, and code-generation is astatine the halfway of the language. Such flexibility, naturally, comes astatine a costs of the instrumentality having difficulties pinch fixed codification analysis. Doesn't mean location are nary bully codification checkers/analyzers — Nagelfar is 1 of the much blase linters disposable for this purpose, but it relies connected syntax tables. So it sees a string, not a usability call. It cannot verify arguments for a bid it doesn't cognize exists yet, for instance, aliases find if a dynamically modified bid is valid until runtime.
Therefore, erstwhile coding successful Tcl, make judge to person a speedy bid reference useful to go proficient.
Limited Multimedia Capabilities
Tcl was ne'er meant for real-time interactive multimedia applications pinch 3D aliases audio.
Sure, location exists a powerful Tcl3D package which provides wrappers for OpenGL, SDL, and FTGL. It’s awesome if you want to constitute a high-performance cross-platform GUI that renders 3D objects, but it's evidently rather a specialized hold aimed astatine high-end engineering, technological research, and business simulation. Canvas3d package is besides disposable arsenic a much high-level instrumentality for akin usage cases.
As for the audio, though Tcl doesn't connection powerful sound playback facilities, you tin easy play activity sounds aliases OS-bundled strategy sounds utilizing OS-provided APIs. Here's an illustration of a elemental one-line timer implementation for Windows (using twapi) and Linux (using PulseAudio paplay). It's non-blocking, truthful you'll request to person the arena loop active. Try moving pinch wish for instance:
# Simple non-blocking timer pinch sound utilizing wish aliases a non-blocking REPL tcl session # Duration group as: min*sec*msec. Timer tin beryllium canceled pinch 'after cancel $timer' # For Windows (using the TWAPI package): package require twapi; group timer [after [expr {1*60*1000}] {foreach d {0 1000 2000 3000} {after $d {twapi::beep -type asterisk}}}] # For Linux (assuming freedesktop sounds are present) set timer [after [expr {1*60*1000}] {foreach d {0 1000 2000 3000} {after $d {exec paplay /usr/share/sounds/freedesktop/stereo/complete.oga &}}}]There are besides community-developed interfaces to utilize the playback capabilities of outer players, pinch extensions for MPV audio player aliases VLC Player available.
No Matrix Multiplication

As a consequence of the "Everything Is a String" paradigm, Tcl isn't perfect for moving pinch specialized information types for illustration vectors aliases matrices. To beryllium clear, these, too, tin beryllium represented arsenic strings, but the rumor is the efficiency. While you can correspond a 1GB matrix arsenic a drawstring aliases a list, you shouldn't because of the representation and CPU overhead of Tcl soul entity management. And though a VecTcl package is disposable for the language, you'll apt conscionable discarded clip trying to do linear mathematics successful Tcl.
Even the EDA/chip manufacture doesn't request Tcl to do the math. There, Tcl is utilized to negociate the information travel betwixt the C++ engines that really do the math. Tcl is valuable for its Regular Expressions and List Manipulation capabilities. In EDA, being capable to parse a 5GB matter record and extract 10 circumstantial ligament names is simply a overmuch much valuable usage lawsuit (and skill) than doing matrix multiplication successful Tcl.
While Tcl’s bytecode compiler is businesslike for logic, it cannot span the spread to SIMD aliases hardware-level matrix optimizations that a dedicated C room provides. If you ever request to do mathematics connected information types wherever the string-representation overhead is simply a bottleneck, either embed C-code consecutive into your Tcl scripts utilizing the aforementioned critcl library, aliases load and telephone functions from a natively compiled library.
Some of you mightiness scoff and reply pinch "Python's amended than your weird string-based bid language"! To which I'd reply that, yes, Python is the king of AI and Matrix math, but present is the truth you mightiness not know: Python doesn't do the math, Python uses NumPy. When you multiply 2 matrices successful Python, the Python expert stops, hands the representation addresses to a highly optimized C/Fortran library, and waits for the result.
And arsenic for JavaScript, it's existent that JS engines for illustration Chrome's V8 are among the astir precocious interpreters successful history. Using JIT compilation, V8 tin often scope 50%–80% of C++ velocity for math: it identifies specified "Matrix Loops" and compiles them to instrumentality codification connected the fly. JS is the fastest interpreted connection for earthy mathematics because of the billions of dollars spent optimizing it. I've thing against JS, I usage it regularly myself. But it does salary for that velocity pinch monolithic complexity and a perpetually shifting ecosystem. Ask immoderate web developer, they'll show you each astir it.
Tcl offers a trade-off: it gives up JIT-speed for a rock-solid, almost 40-year unchangeable C API that makes "gluing" a C++ matrix motor to build CLI and cross-platform GUI apps quicker than successful almost immoderate different language. It's wherever you spell erstwhile the matrix calculation is done, and you conscionable request a UI to show the result, tweak the parameters, aliases quickly and efficiently parse a elephantine log record successful a "functional style" using the generator package that comes pinch the modular Tcl library.
"Ugh… Tcl Is So Annoying!"
With the afloat image now successful view, it's easy to recognize why Tcl is specified a misunderstood language, and really missing immoderate of the basal aspects of Tcl's architecture and syntax tin consequence successful frustration.
For instance, you tin find group connected the web expressing their annoyances:
"Everything is string" is the astir annoying portion astir TCL. It astir apt caused much headaches than everything else, combined, particularly erstwhile you are not alert of each the pitfalls.
Or:
I work together that it tin beryllium annoying, and it makes it really challenging to create much precocious information structures. It does make it awesome for tinkering though.
I powerfully judge the vexation these commenters acquisition mightiness beryllium a denotation of utilizing Tcl incorrectly.
If you dainty Tcl simply for illustration a drawstring processor, you get headaches. If you effort to instrumentality precocious information structures pinch strings, you'll discarded your time. Tcl is simply a merchandise of its clip erstwhile it comes to syntax, because of its terminal-oriented origin.
Don't overcomplicate things by attempting to make Tcl do everything. I must confess that I besides tried that astatine immoderate point. Having acquisition pinch C# List<T>, Dictionary<K,V>, and C++ std::vector, doing superior mathematics successful Tcl felt for illustration programming pinch oven mitts. Thankfully, I quickly realized my correction and now person much realistic, grounded expectations of Tcl.
Tcl is simply a cross-platform "glue" language. Think of it arsenic a awesome "manager's" tool, for it excels at:
- Control and orchestration (coroutines, threads, scripting interfaces and embedding)
- Event-driven patterns (I/O and events)
- Presentation (Tk GUI, aliases CLI pseudo-GUI)
- Testing and automation
These are immoderate of the usage cases wherever Tcl is objectively useful moreover today, contempt of its "age".
Many expression-based languages (like C#, C++, JS) let adaptable assignments almost anywhere, including if-else blocks. In the pursuing illustration immoderate personification automatically is fixed an admin role. If this were missed astatine codification reappraisal shape and made its measurement to production, it would beryllium a disaster. Some compilers pass astir this. Some don't. It's a chaotic westbound without immoderate clear rules.
if(name != "" && user_role = "admin") { // ... do something }With Tcl you person to intelligibly asseverate that you want to assign a value to a variable:
set myVariable "new value"
Or only read the worth pinch $ aliases [set]:
puts $myVariable aliases group myVariable
Moreover, successful Tcl, the if bid expects an expression. Because group is simply a bid and not an operator, it cannot beryllium people embedded wrong a modular Tcl look without definitive nesting. This makes it virtually intolerable to "accidentally" delegate a adaptable successful a loop aliases an if-else block, unless you're really drunk. You would virtually person to spell retired of your measurement to replicate the C#-example:
if {$name ne "" && [set user_role "admin"] eq "admin"} { # This requires *intentional* effort, it's ne'er a typo }BUT!
Does this mean Tcl is perfectly safe by design? Of people not!
For instance, successful Tcl, if you hide to brace an expression, the expert mightiness measure the contents of a adaptable arsenic code. If an look isn't braced {...}, Tcl performs a information of substitution before passing the contents to expr, perchance executing the bracketed command:
# DANGEROUS: Unbraced expressions # Imagine if this contained "exec rm -rf" instead! set user_input {[puts -nonewline PWNED!; return -level 0 1 ]} # This would *execute* $user_input aft adaptable substitution! set consequence [expr $user_input eq 1]; # => "PWNED!1" # This results successful the aforesaid vulnerability owed to the usage of quotes set consequence [expr "$user_input eq 1"]; # => "PWNED!1" # Whereas passing the assemblage arsenic a braced literal is safe set consequence [expr {$user_input eq 1}]; # => "0"Ergo, you should always brace {…} your expr, if aliases immoderate different conditions and expressions. Bracing prevents the Tcl expert from substituting the adaptable earlier it reaches the command.
Finally, Tcl allows you to destroy immoderate command, including the "default" ones for illustration puts, by renaming a bid to "". You can't do that to a reserved connection successful C, C++, C#, JS aliases Python, of course, but the truth that you tin successful Tcl, doesn't needfully mean that it's ever a bully idea.

Learning Resources
If you deliberation you're fresh to prime up Tcl/Tk, aliases request to study conscionable the basics of Tcl successful bid to usage Tk, there's plentifulness of knowledge to beryllium found. Except… Finding thing applicable to the latest versions of Tcl/Tk whitethorn beryllium challenging owed to the sheer magnitude of books and articles written connected Tcl successful its almost 40 years of existence. Many books disposable on-line are either highly outdated aliases don't specify which type of Tcl they use to, which doesn't help. Here are the resources I relied connected to study Tcl/Tk, and tin urge to anyone starting retired pinch Tcl/Tk:
- "The Tcl 9 Programming Language A Comprehensive Guide" by Ashok P. Nadkarni. A superb book written by a superb technologist that covers axenic Tcl (without Tk). It's benignant of considered "overkill" for "casual" Tcl users because it covers a batch of precocious topics. Doesn't mean you should skip it! Quite the contrary, this should beryllium your first book connected Tcl 9.0. It's highly precise pinch concepts and explanations. But, boy, does Mr. Nadkarni emotion to condense his explanations truthful overmuch that they request to beryllium unpacked 2- aliases moreover 3-fold! For instance, by the end, successful the "Coroutines" section, I was really struggling pinch the examples given, and had to painstakingly spell though them statement by line, to understand what was happening, and astir importantly — why. Simply moving the examples successful the tclsh console wasn't helping much, since the results would simply lucifer those successful the book. So I had to spot debug messages everyplace to understand the flow. To maine (admittedly not sharpest instrumentality successful the shed) a batch of those seemed "too clever" and if implemented successful existent programs could extremity up generating important attraction indebtedness simply owed to galore of them being purely "academic". Again, it really is 1 of the much blase books connected Tcl, and if you ever determine to earnestly prime up the language, you'll person to spell though the book astatine slightest twice, to get a amended grasp of the afloat capabilities of Tcl 9. I highly recommend it
- TkDocs — arsenic mentioned previously, an excellent up-to-date root of accusation connected Tk. Teaches the correct, modern concepts and specifically highlights outdated techniques and devices to avoid.
- Tcl/Tk astatine Tutorialspoint — I don't urge this source, since it seems to beryllium excessively dated. It consistently pops up among the top-10 results erstwhile you hunt for a "Tcl/Tk Tutorial", which is simply a shame. It's still talking astir getting your installer from ActiveState (!) which is everlastingly stiff successful clip arsenic an ancient Tcl 8.6.14 version, arsenic good arsenic teaches immoderate bad coding practices, for illustration utilizing expr without bracing the expressions

- tclsh and wish — yes, the interpreters themselves. As covered earlier, Tcl offers adjuvant hints connected really commands should beryllium used. Simply type successful a bid you want to usage but don't retrieve the correct statement bid — for illustration regexp — and get a hint:
While reference the guides and the books, I highly urge trying retired the commands successful an interactive Tcl session. Type the commands in, modify the examples, research freely, to get a overmuch amended knowing of the concepts, architecture, commands and their usage cases.
As for the quality betwixt the 2 interpreters — refer to the earlier section.
Where to Get Tcl/Tk
Here's really you tin get your hands connected this fascinating marvel of quality package engineering.
Tcl/Tk for Windows
Ashok P. Nadkarni provides his ain distribution of Tcl/Tk — Magicsplat Tcl/Tk for Windows. It contains astir communal and useful packages you'll request to create the immense mostly of your CLI and GUI tools.

During installation, make judge to take the "Advanced" installation option and instal Tcl/Tk for all users, if it's imaginable connected your system. This way, the installer will create each basal strategy paths truthful you'd beryllium capable to commencement tclsh and wish from anywhere, arsenic good arsenic registry ".tclapp" and ".tkapp" extensions for speedy book execution from the Explorer, a very invited value of life feature.
Tcl/Tk for Linux
The easiest measurement to commencement utilizing Tcl/Tk connected Linux is to cheque if it's already disposable by typing tclsh successful the console. Chances are, it's already there. If it is, you tin widen the installation pinch the Tk toolkit, arsenic good arsenic immoderate communal extensions. Here's an illustration for distros that usage apt arsenic a package manager:
sudo apt-get instal tcl tk libsqlite3-tcl sqlite-tcl tcltls tcllib libtk-img tcl-thread
The catch?
Tcl/Tk type and the disposable extensions dangle connected your distro's repositories. For instance, since Tcl 9.0 is comparatively new, it didn't make it into repos of the existent (at the clip of writing) LTS builds of various Linux distributions. So if you do this connected thing for illustration Debian, Ubuntu aliases Mint aliases moreover Arch Linux, you mightiness get Tcl 8.6. For the mostly of casual Tcl/Tk coders this mightiness beryllium bully enough.
But, if you want a bleeding edge, "batteries included" Tcl/Tk version 9 package pinch each bells and whistles, I person immoderate bully news!
Jump complete to my forked version of Ashok P. Nadkarni's experimental repo pinch workflows for generating Tcl batteries-included distributions utilizing BAWT.

The forked repo began arsenic a measurement to simply adhd soft font rendering support to the Linux x86_64 Tcl/Tk builds, arsenic successful the original repo the very first builds lacked this. Then, while I was looking for a Linux distro to usage pinch my mini single-board computer, I quickly realized that the immense majority, if not all, of distros for Arm SBCs still vessel pinch the outdated Tcl 8.6. Thus, I extended the repo *with an Arm64 Linux build, and a elaborate group of instructions connected really to instal this Tcl/Tk 9 Batteries Included bundle either per-user, aliases system-wise on Linux.
So if astatine immoderate constituent you consciousness for illustration you're fresh to jump connected the Tcl/Tk 9 band-wagon, download a Linux build from here, and follow this Guide connected really to instal this bundle. It's the 1 I'm using, arsenic you mightiness expect, and it's been serving maine well.
As for the compatibility pinch Wayland successful peculiar — successful my experience, Tcl GUI widgets activity conscionable good successful Wayland-based Desktop Linux distros for illustration Zorin OS 18, truthful you're not forced to usage Xorg/X11, and tin safely codification your adjacent cross-platform GUI tool.
IMO, the Linux type of an "all-in-one" Tcl/Tk 9 package is particularly valuable and noteworthy, because Linux tin beryllium recovered almost anywhere. Sort of for illustration DOOM. If thing tin footwear into Linux, it tin tally Tcl and possibly moreover Tk. And if a Tcl/Tk installation ships pinch sqlite, Thread, tls, json and different communal packages — it tin do anything.
Here's a speedy demo of the aforementioned Tcl 9.0.3 Arm64 "batteries included" distribution moving a bunch of scripts successful an Arch Linux-based OS, connected a miniature single-board computer.

Tcl/Tk for macOS
Here's the deal: macOS is simply a very "opinionated" OS. Which makes sense, since Apple holds a tight grip complete it down to the infinitesimal detail. They besides don't awkward distant from changing it drastically from type to version, often breaking backwards compatibility. For instance, Apple will phase retired their Rosetta 2 emulator starting pinch macOS 28. Rosetta is what allowed Macs pinch Apple silicon to tally apps that were built for Macs pinch an Intel processor (x86 architecture) by translating codification connected the fly. Therefore, you first request to determine which Tcl build you're aft — Intel aliases M-series/ARM one. Most apt it's the latter, truthful let's move on.
Believe it aliases not, but Apple really does vessel Tcl pinch macOS. The rumor is — the type they vessel is the embarrassingly ancient Tcl 8.5, making love backmost to 2010s. That's why upon moving it you'll spot a disclaimer:
WARNING: This type of tcl is included successful macOS for compatibility pinch bequest software. In early versions of macOS the tcl runtime will not beryllium disposable by default, and whitethorn require you to instal an further package.
Which not only intends you perfectly need to switch it, you'll besides person to conflict this default installation, arsenic the strategy paths constituent to this bequest bundle. If you effort to tally modern Ttk GUI codification connected it, it will apt clang aliases look for illustration a Windows 95 app astatine best. This is why you must dainty the strategy Tcl arsenic useless discarded and debar it. And if it's already missing wholly successful your latest type of macOS — you're successful luck, and now only request to instal the modern Tcl/Tk binaries.
There are respective ways to instal Tcl/Tk 9.0 connected macOS:
- Homebrew — benignant of a "standard" measurement to instal the latest Tcl/Tk connected Macs. It installs to /opt/homebrew and tries to merge amended into the OS. It is simply a simpler install, but tin occasionally break erstwhile Apple makes awesome OS changes (allegedly)
- MacPorts — while Homebrew is the celebrated choice, MacPorts is technically a much "self-contained" option. It installs everything Tcl into /opt/local, doesn't effort to usage Apple’s strategy libraries, and alternatively brings its ain versions of extensions (OpenSSL, SQLite, etc.). This makes it ace unchangeable but results successful longer instal times because it often compiles from source. MacPorts is apparently besides celebrated for its dependency hell: if you want to instal Tcl, it mightiness determine to instal 40 different packages first, and the resulting Tcl/Tk instal will return overmuch much disk space. Fun fact: MacPorts itself is mostly written in Tcl
- BAWT builds — the aforesaid "batteries included" portable bundle compiled by Ashok P. Nadkarni, isolated from for macOS. He admits that Mac bundle is mostly untested. Available here. Also, conscionable for illustration pinch the Linux BAWT builds, it's your work to group up each paths, limitations and everything else, to make it really function, and beryllium capable to moreover observe its ain libraries. This is the astir "experimental" action mostly for cases erstwhile everything other failed, nary thought whether it useful aliases not
I went pinch the Homebrew variant, since macOS is not my regular driver and I conscionable needed thing elemental to beryllium capable to instal and trial my Tcl/Tk apps connected Mac.
Here's really to instal Tcl/Tk 9.0 On macOS pinch Homebrew.
First, you request to instal Homebrew. Copy and paste this into your terminal. It will instal the package head and, if needed, the Apple Command Line Tools. At the extremity of the process, travel the guideline connected which commands to tally successful the terminal successful bid to adhd Homebrew to strategy PATH:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"Once "brew" is ready, instal Tcl 9.0:
# Install Tcl/Tk 9.0 core brew instal tcl-tk # Install other extensions and limitations if needed brew instal tdom openssl@3Finally, you request to show your M-series Mac to usage the latest type of Tcl, not the 2010 one. Run this to adhd the Homebrew way to your ammunition configuration, including other paths to beryllium capable to compile C packages from root successful the future:
cat << 'EOF' >> ~/.zshrc # Homebrew tcl-tk environment export PATH="/opt/homebrew/opt/tcl-tk/bin:$PATH" export LDFLAGS="-L/opt/homebrew/opt/tcl-tk/lib $LDFLAGS" export CPPFLAGS="-I/opt/homebrew/opt/tcl-tk/include $CPPFLAGS" export PKG_CONFIG_PATH="/opt/homebrew/opt/tcl-tk/lib/pkgconfig:$PKG_CONFIG_PATH" EOF source ~/.zshrcTo verify, create a test.tcl record pinch the pursuing contents, and tally it pinch tclsh test.tcl to cheque the output:
# test.tcl puts "Tcl Location: [info nameofexecutable]" puts "Tcl Version: [info patchlevel]" foreach pkg {sqlite3 Thread tls tcllib tdom} { if {[catch {package require $pkg} ver]} { puts "\[-\] $pkg: MISSING" } other { puts "\[+\] $pkg: Version $ver" } }Note: if you ever find a circumstantial package you request missing, first cheque if Homebrew carries it by searching formulae.brew.sh aliases moving brew hunt <name> successful the console. If it isn't listed, you will request to compile aliases instal it manually into the tcl-tk environment.
You're now fresh to create GUI apps successful macOS without Apple's blessing, platform-specific tools. Or 💵 fees.
Tcl/Tk IDEs
I person briefly mentioned the Integrated Development Environment facet of moving pinch Tcl/Tk.
VSCode (or alternatively it's fork — VSCodium) is the IDE of my choice, arsenic location are 2 extensions disposable for it, which make coding successful Tcl easier:
- Tcl Navigation and Tools — for syntax highlighting and codification snippets
- Code Runner — arsenic a unified measurement to tally Tcl codification wrong the IDE while coding
Not overmuch to adhd there. This should beryllium capable for the mostly of Tcl/Tk devs not presently employed astatine immoderate EDA company, wherever they'd astir apt beryllium provided pinch specialized tools, tailored for bespoke versions of Tcl utilized successful the spot industry. I've developed dozens of CLI and GUI devices utilizing VSCodium, and it's been a comfortable capable experience.

To that, I tin adhd that location beryllium different editors, for instance:
Alited — "A Light Editor", a genuinely peculiar project. Firstly, it's written wholly successful Tcl/Tk. Then, it provides immoderate precocious devices for task and codification room guidance and moving connected respective projects astatine once, pinch immoderate functionality specifically tailored for Tcl development.

I tried utilizing it for a while, and moreover for maine it felt excessively "old-school" some successful look and function, truthful I went backmost to VSCodium.
As for the truth that Alited is wholly built successful Tk — it's an awesome demo of what tin beryllium achieved pinch Tcl/Tk, but makes the editor look and feel… odd. It's not rather OS-native, and the hotkeys are very "opinionated" and felt unorthodox. It was forged successful a different era of development, and it remains committed to a workflow that modern alternatives person since smoothed over. For codification study and autocompletion moreover the Tcl-centric Alited mostly offers basal autocompletion functionality, akin to that of the aforementioned VSCode extensions:
If Tab cardinal is pressed astatine $ (or $: or $::), the completion database would see only the variables of existent proc/method and (if $: or $::) Tcl world variables.
All successful all, I recovered Tcl/Tk coding acquisition successful VSCodium comfortable enough. Spend conscionable a small spot to group up a bully IDE, and you'll want to codification successful Tcl non-stop.
Afterword
Embrace boring technology.
To circle backmost to the opening of this guide, my travel started pinch a elemental hunt for a cross-platform UI framework. With Tcl/Tk I recovered truthful overmuch much than that. I sewage my hands connected a robust, time- and battle-tested, cross-platform and highly versatile "manager's helper" tool-set. I besides discovered a caller world of programming languages, wherever codification and information are interchangeable, which made maine look otherwise astatine programming arsenic a hobby and arsenic a profession. It inspired me.
Learning Tcl/Tk and processing devices pinch it felt almost surreal. Like going backmost to the times of bully aged reliable technology, 1 that doesn't alteration simply because immoderate task manager, aliases a tech lead, had decided their stack needed much "bling", aliases that it had to abruptly drawback up to the younger, much agile, yet overmuch little tested devices and concepts, breaking backwards-compatibility and causing panic attacks for their developer- and user-bases.
I person to beryllium honorable though: Tcl lacks immoderate meaningful organization isolated from the squad of its halfway maintainers. So utilizing it whitethorn make you consciousness "lonely", simply because it's not a trendy language. It's a connection of mini teams, aliases professionals utilizing it to build pragmatic tools, aliases activity pinch mission-critical systems "in the background". They worth reliability and consistency complete the dynamically-changing ecosystems for illustration Python, Node.js, JavaScript aliases TypeScript. They slumber well, knowing that successful a week, successful a year, aliases moreover successful a decade, their codification will tally conscionable arsenic it did before. They attraction connected solving existent problems alternatively of fighting their ain toolchain, and softly support the systems that the remainder of the world takes for granted.
Could this beryllium thing you've been looking for?
Thank you for reading.
Useful Links
- https://colin-macleod.blogspot.com/2020/10/why-im-tcl-ish.html
- https://github.com/vivus-ignis/the-art-of-the-terminal/tree/master/most-misunderstood-proglang
- https://dontbreakprod.com/posts/embrace-boring-technology
- https://www.tcl-lang.org/man/tcl8.5/tutorial/Tcl0.html
- https://wiki.tcl-lang.org/page/Tcl+Tutorial+Index
- https://www.tcl-lang.org/man/tcl9.0/index.html
- Tcl/Tk Video YouTube channel
English (US) ·
Indonesian (ID) ·