When Everything Is a String: TCL's Peculiar Design
TCL treats code, data, and types as strings. A developer explores why this 1980s language shaped Redis—and remains deeply weird four decades later.
Written by AI. Bob Reynolds
February 28, 2026

Photo: Dreams of Code / YouTube
Most programming languages ship with macOS by default because Apple needs them. TCL ships with macOS despite Apple wanting to remove it. That tension—between necessity and obsolescence—captures something essential about this language that almost nobody uses anymore but continues to influence software millions depend on daily.
A developer recounts encountering TCL, or Tool Command Language, in their first finance role. Fresh from learning C++, they found themselves working with a language that treats everything—types, expressions, commands, even control flow—as strings. Not metaphorically. Literally.
"When I say everything in Tickle is a string, I mean quite literally everything," they explain. "This includes things you might expect, such as every type being a string, but it also includes some other things you may not necessarily think of, such as complex data structures and even the syntax of the language itself."
This design choice creates a programming environment unlike anything mainstream developers encounter. It's not that TCL is old—many languages from the 1980s remain current. It's that TCL's fundamental architecture diverges so sharply from conventional design that learning it requires unlearning assumptions that feel universal.
The Two Faces of Strings
TCL uses both quotation marks and braces to define strings. The distinction matters enormously. Quotes trigger variable substitution—"$name" becomes whatever value name holds. Braces preserve literal text—{$name} stays exactly as written.
This isn't syntactic sugar. It's the mechanism that controls when code executes. A while loop defined with quotes evaluates its condition once, substituting variables immediately. The same loop with braces re-evaluates each iteration. The difference between an infinite loop and working code comes down to which delimiter you choose.
The developer demonstrates this with a simple counter loop. Using quotes for the conditional expression freezes the tested value at zero. The loop never terminates because it's not actually checking the variable—it's checking the string "0" that replaced the variable when the parser first encountered it. Switch to braces and the code works as intended.
For programmers accustomed to syntax being syntax and data being data, this feels upside-down. The choice between braces and quotes isn't about formatting or readability. It's about execution timing. The same string, delimited differently, produces entirely different program behavior.
Commands Masquerading as Statements
TCL has no statements. What looks like an if statement is actually a command that takes two string arguments: a boolean expression and code to execute if true. What looks like a function definition is the proc command consuming strings that describe parameters and implementation.
This means you can store an if command in a variable. You can store the code that the if evaluates in another variable. Then you can execute both dynamically using eval. The line between code and data doesn't blur—it doesn't exist.
"Everything in tickle is a string. I really cannot stress that enough. This also includes commands such as the eval command itself," the developer emphasizes.
This design enables metaprogramming so powerful it circles back to dangerous. Functions become strings that can be inspected, modified, and executed at runtime. The entire language surface area becomes programmable because the language itself is just structured text.
Redis creator Salvatore Sanfilippo saw this design and recognized something valuable. The first version of Redis—originally called LMDB—was written in TCL. The command structure, the way data types are represented, the string-centric design philosophy: all inherited from TCL. When Sanfilippo rewrote Redis in C, he kept the conceptual model.
Data Structures Built from Whitespace
Lists in TCL are strings with elements separated by spaces. Dictionaries are strings with alternating keys and values separated by spaces. There's no list type or dictionary type—just conventions for how to structure strings and commands for operating on those conventions.
To create a list element containing a space, you nest delimiters. Quote a string inside braces, or use the list command to handle the delimiter dance automatically. Lists can contain lists because a properly delimited substring is just another space-separated structure.
This approach seems fragile until you notice it's also extraordinarily flexible. Adding a new data structure doesn't require modifying the language—just defining new conventions for string interpretation and commands for manipulating them. The language doesn't need to know about your data structures. You teach it by writing commands that understand your conventions.
Modern developers might call this "stringly typed" as a pejorative. But TCL predates that criticism by decades and doesn't apologize. The string-centrism is the point, not a compromise.
Explicit Scope in a World of Conventions
Most languages infer scope from context. Variables defined inside functions stay local unless explicitly exported. Global variables are accessible everywhere unless shadowed. TCL reverses this.
Define a global variable, then reference it inside a function. The code fails. TCL doesn't assume you want global access just because you used a global variable. You must explicitly declare global x inside the function to link the names.
The same applies to reference semantics. Passing a variable to a function passes a copy. To modify the original, you use upvar to explicitly bind a local name to a calling-scope variable. The developer notes: "We've explicitly defined the scope of X to be passed in by reference."
This explicitness violates decades of language design evolution toward implicit behavior. Modern languages pride themselves on inferring programmer intent. TCL assumes nothing and requires you to state everything.
The ergonomic cost is real—more boilerplate, more opportunities for confusion. The benefit is precision. Reading TCL code, you know exactly which scope every variable reference targets. There's no hunting through outer scopes or consulting rules about closure capture. The code says what it means because the language requires it.
The Language That Persists
TCL peaked in the 1990s when its Tk GUI toolkit made cross-platform graphics programming tractable. Then Java, Python, and JavaScript offered better abstractions with larger communities. TCL usage collapsed but never disappeared.
It persists in test automation, where its string-centric design excels at generating and manipulating test cases. It persists in embedded systems, where its small interpreter footprint matters. It persists in Redis's design DNA, shaping how millions of developers think about data structure servers without knowing TCL exists.
Mostly it persists as an example of an alternate path not taken. What if we designed languages around text manipulation instead of type systems? What if we made scope explicit instead of inferred? What if we collapsed the distinction between code and data entirely?
These questions matter more than TCL's market share suggests. Every generation of developers rediscovers these design tensions. The answers TCL provides aren't fashionable, but they're considered. Four decades of production use have validated at least some of the choices.
Whether TCL remains installed on your Mac much longer doesn't really matter. The ideas it embodies—that strings can be a universal representation, that explicitness has value, that treating code as data enables powerful abstractions—those ideas keep surfacing in new contexts.
Redis isn't going anywhere. And Redis is TCL's most successful legacy: proof that deeply weird design choices, consistently applied, can produce something elegant enough to matter.
Bob Reynolds is Senior Technology Correspondent for Buzzrag.
Watch the Original Video
The weirdest programming language I ever learned
Dreams of Code
24m 15sAbout This Source
Dreams of Code
Dreams of Code is a rapidly growing YouTube channel that has attracted 194,000 subscribers since its inception in mid-2025. The channel is dedicated to providing concise, four-minute tutorials designed to enhance the skills of both novice and intermediate developers. With a focus on practical, project-driven learning, Dreams of Code delves into a variety of programming languages and tools, appealing to audiences interested in both mainstream and niche technologies.
Read full source profileMore Like This
AI Models Now Run in Your Browser. That Shouldn't Work.
Transformers.js v4 brings 20-billion parameter AI models to web browsers. The technical achievement is remarkable. The implications are just beginning.
Anthropic's Three Tools That Work While You Sleep
Anthropic's scheduled tasks, Dispatch, and Computer Use create the first practical always-on AI agent infrastructure. Here's what actually matters.
Dokploy Promises Vercel Features at VPS Prices
A new tool claims to deliver platform-as-a-service convenience on cheap VPS infrastructure. Better Stack demonstrates what works and what doesn't.
Decentralized Tech: Gadgets for the Privacy-Conscious
Explore gadgets that blend tech and anarchism to maintain privacy and autonomy in a surveilled world.