🧵 Introduction to Tcl, ECL, and Janet
Tcl (Tool Command Language) is a minimalist scripting language designed in the late '80s to be easy to embed and extend. It’s used in automation, GUIs (via Tk), and scripting embedded systems.
ECL (Embeddable Common Lisp) is a full implementation of Common Lisp that can be embedded into C/C++ applications. It gives you the expressive power of Lisp — including macros and runtime code generation — with the ability to compile to native code.
Janet is a modern, lightweight Lisp-like language designed for embedding, scripting, and systems programming. It features a clean syntax, fibers for concurrency, and a self-contained virtual machine under 1MB in size.
🔧 Comparison Table: Tcl vs ECL vs Janet
Aspect | Tcl | ECL (Embeddable Common Lisp) | Janet |
---|---|---|---|
Paradigm | Imperative, scripting | Multi-paradigm: functional, imperative, OO | Functional, imperative, data-driven |
Design Origin | Scripting for embedding | Full Lisp implementation embeddable in C | Modern Lisp-like scripting, embeddable |
Syntax Style | Command-oriented (prefix, no infix) | Lisp s-expressions | Lisp-style, but with own bytecode VM syntax |
Embeddability | Excellent, designed for it | Good, but heavier than Janet/Tcl | Excellent, self-contained with C API |
FFI / Interop | C bindings via Tcl API | Full C FFI, but verbose | Simple C interop with abstracted memory model |
Typing | Dynamically typed | Dynamically typed, powerful type system | Dynamically typed, extensible types |
Meta-programming | Limited | Macros, reader macros, reflection | Macros, fibers, metaprogramming native |
Concurrency | Event-driven, coroutines possible | Multi-threaded, but concurrency is tricky | Fibers (green threads), channels |
Execution Model | Interpreter | Compiles to C or bytecode | Compiles to bytecode and JITs |
Tooling | Mature, old-school tools | Compiler, debugger, some REPLs | REPL, build tools, bundling |
Performance | Moderate, mostly interpreted | Good, compiled to native via C | Very fast for an embedded language |
Binary Size | Small-medium | Medium-large | Small (<1MB with full stdlib) |
Deployment | Tcl scripts or app | Can build standalone binaries | Easy deployment, self-hosting |
Learning Curve | Low (but idiosyncratic syntax) | Medium-high (Lisp mindset) | Low-medium (simpler than full Lisp) |
Community / Ecosystem | Legacy, enterprise, Tk GUI, NetOp | Academic, embedded Lisps, some OSS | Growing hacker/systems community |
GUI Support | Tk (native GUI toolkit) | Through C FFI or SDL bindings | SDL2 bindings, or embed GUI engine |
REPL Availability | Yes | Yes (when built) | Yes, excellent interactive shell |
🧠 Principles Behind Each Language
🟨 Tcl – “Everything is a Command”
-
Philosophy: Simplicity over consistency. It’s meant for ease of use by system integrators and domain experts, not necessarily programmers.
-
Strengths:
- Embedding in C is simple.
- Excellent for glue code and DSLs.
- Very readable for simple scripts.
-
Weaknesses:
- Lack of strong abstraction tools.
- Syntax can get awkward for complex programs.
- Performance isn't great.
Principle: Minimalism in core semantics enables flexibility, but limits abstraction.
🟩 ECL – “Lisp, Anywhere”
-
Philosophy: Bring the power of Common Lisp (CL) to embedded or lightweight environments.
-
Strengths:
- Full CL standard (macros, CLOS, etc.).
- Can compile to standalone C code.
- Ideal for high abstraction + runtime code generation.
-
Weaknesses:
- Heavyweight runtime compared to others.
- Harder to embed than Janet or Tcl.
- Limited mobile/wasm support.
Principle: Expressive power through homoiconicity and metaprogramming trumps simplicity.
🟦 Janet – “Tiny, Modern Lisp for Systems”
-
Philosophy: Combine the power of Lisp and modern features into a minimal, fast, embeddable language.
-
Strengths:
- Lightweight and fast (tiny VM).
- Native concurrency primitives (fibers, channels).
- Bytecode + JIT + extensibility.
- Very good for embedded tools, CLI apps, and scripting.
-
Weaknesses:
- Not as mature as Tcl or CL.
- Less documentation and libraries.
- Non-traditional syntax may throw off experienced Lisp users.
Principle: Minimalism plus composability with modern ergonomics makes a “hackable” joy.
🧭 When to Use Which?
Use Case | Tcl | ECL | Janet |
---|---|---|---|
Embedding in C app | ✅ Excellent | ✅ Good, but heavier | ✅ Excellent and tiny |
Scripting GUI apps | ✅ With Tk | ❌ Not first-class | 🟡 Possible with bindings |
DSLs / Configs | ✅ Designed for this | ✅ Macros powerful | ✅ Lightweight DSLs |
Educational / Lisp learning | ❌ Too idiosyncratic | ✅ Excellent for Lisp learning | ✅ Friendly Lisp starter |
Runtime Metaprogramming | ❌ Very limited | ✅ Top-tier | ✅ Excellent |
Performance-sensitive apps | ❌ Slow | ✅ Compiled to native | ✅ Good, even in interpreted mode |
Tiny CLI Tools / Utilities | 🟡 Possible | ❌ Too big for this | ✅ Perfectly suited |
Concurrency-heavy tools | 🟡 Event loop-based | ❌ Not great | ✅ Built-in fibers, async |
🔍 Summary of Design Trade-offs
Principle | Tcl | ECL | Janet |
---|---|---|---|
Simplicity vs Power | Tcl leans hard into simplicity | ECL leans fully into expressive power | Janet balances both |
Minimalism vs Completeness | Tcl minimalistic core | ECL complete Common Lisp suite | Janet minimal core with extensibility |
Syntax Philosophy | Commands + strings | Code = Data (s-exprs) | Lisp-like, but bytecode aware |
Embedding Trade-off | Super easy, but limited abstraction | Powerful, but heavy | Easy + powerful + lightweight |
Deployment Trade-off | Simple script runner | Requires build and setup | Tiny self-contained executable |
🧠 Final Thoughts: What Makes Each Good or Bad?
✅ Tcl is good if:
- You want to quickly script something in an app.
- You need to embed a config/system script engine.
- You're maintaining older systems or GUI apps with Tk.
But bad if: You want modern concurrency, abstraction, or high performance.
✅ ECL is good if:
- You need real Common Lisp in an embedded or portable format.
- You want powerful metaprogramming and macro support.
- You don’t mind a larger runtime or more complex build.
But bad if: You care about binary size, fast embedding, or simpler syntax.
✅ Janet is good if:
- You want something between Lua and Lisp with modern features.
- You're building tools, CLIs, servers, or config systems.
- You want concurrency, speed, and embeddability.
But bad if: You need full Common Lisp or established libraries and frameworks.
🧰 Installing Tcl, ECL, and Janet
Here are quick install steps for Debian, Alpine Linux, and Termux (Android).
⚠️ Disclaimer: These commands are untested on your system. If you try one, please let me know how it goes so I can update this guide!
🟨 Tcl
Debian / Ubuntu:
sudo apt update
sudo apt install tcl
Alpine Linux:
apk add tcl
Termux (Android):
pkg update
pkg install tcl
✅ Should give you tclsh
, the Tcl shell (REPL).
🟩 ECL (Embeddable Common Lisp)
Debian / Ubuntu:
sudo apt update
sudo apt install ecl
Alpine Linux:
apk add ecl
Termux:
⚠️ Not available in standard packages. Try building from source:
pkg install clang make git readline
git clone https://gitlab.com/embeddable-common-lisp/ecl.git
cd ecl
./configure
make
make install
⏳ Build may take time. You’ll get the ecl
shell and libraries.
🟦 Janet
Debian / Ubuntu:
sudo apt update
sudo apt install git build-essential
git clone https://github.com/janet-lang/janet.git
cd janet
make
sudo make install
Alpine Linux:
apk add git build-base
git clone https://github.com/janet-lang/janet.git
cd janet
make
make install
Termux:
pkg update
pkg install git clang make
git clone https://github.com/janet-lang/janet.git
cd janet
make
make install
📦 Installs the janet
REPL and lets you run .janet
scripts.
✅ Let Me Know What Works!
These install instructions are rough and untested across environments. If you try them on your system (especially Termux or Alpine), I’d love to hear:
- Did the install succeed?
- Were there any missing dependencies?
- Was the REPL functional?
Your feedback can help others follow a smoother path.