One Language To Rule Them All
2026-06-03
TL;DR The functional paradigm is not general-purpose. It is specialized notation aimed at classical physics and mathematics. We mistook it for a universal foundation and spent decades bolting onto it everything it was not designed to handle. The result is bloat — measured in orders of magnitude — and a habit of removing things that worked, replacing them with things that didn’t, and quietly re-introducing the originals under new names. We never measured whether any of this was worth it.
L;R
SICP — Structure and Interpretation of Computer Programs — is scaffolding for SICM, Structure and Interpretation of Classical Mechanics. SICM uses a programming notation to express classical mechanics. SICP builds that notation. Operating systems are scaffolding for SICP — they provide the environment in which functional programs run. Each layer solves a narrow problem well. None of it is general-purpose. It is a stack of specialized tools aimed at one target: mathematical, functional, deterministic computation.
This would be fine if we had recognized it for what it is. We did not. We declared the functional paradigm the foundation of all programming. When C made functional calling conventions standard practice for every subroutine, we stopped noticing that other conventions existed. The paradigm suited to one class of problems became the only paradigm considered for any problem.
The functional domain has a precise set of properties.
Equations are timeless. Functions are pure. State does not exist. Time does not pass. Order is irrelevant.
Classical physics is built on exactly these properties. SICP provides a notation for reasoning within these constraints. SICM uses that notation to express classical mechanics directly. The fit is exact because the domain and the notation share the same assumptions.
This is not how computers work. This is not how the internet works. This is how a single CPU works. This is how a single node in a network works. The moment you connect two CPUs, or two nodes, time reappears. State reappears. Order matters again. The functional paradigm describes one part of the machine accurately and says nothing useful about what happens between parts.
The problems began when we stretched the notation beyond its target. Re-assignment was the first major addition. In a pure functional system a variable holds one value. In the real world things change. Rather than reaching for a different notation suited to expressing change — state machines, for instance — we bolted re-assignment onto the functional paradigm. The guarantees the notation had offered began to erode. We did not get a more general tool. We got a worse functional tool with state stapled to the side.
Dynamic binding followed the same pattern. Early Lisp resolved variables at runtime, based on the call environment at the moment of execution. This was judged unpredictable and replaced with static scoping — variables resolved at compile time, based on position in the source. Static scoping made compile-time analysis possible. Type checkers and optimizers depend on it. The trade seemed like progress. Then we introduced JIT compilation. JIT makes decisions at runtime, based on what the program is actually doing. That is runtime dynamism. That is what dynamic binding did. We removed runtime flexibility in the 1960s, spent decades building tooling that assumed its absence, then spent more decades building tooling to recover it. Lisp 1.5 had this in 1962. We took a very long detour back to the same place.
Lisp 1.5, in 1962, did the core job in approximately 27 kilobytes. It had garbage collection, recursion, and a runtime evaluator. It was powerful enough to run a proof assistant. VisiCalc, in 1979, invented the spreadsheet paradigm in 20 kilobytes. Unix V1, in 1971, ran the entire operating system — kernel, shell, tools — in 24 kilobytes of total memory. Turbo Pascal 1.0, in 1983, delivered a complete IDE — editor, compiler, linker, all libraries — in a single 38-kilobyte file. These were not toys. They changed the world.
Today we speak in gigabytes. The Linux kernel exceeds 30 million lines of source code. A modern language runtime requires hundreds of megabytes before a user program loads. The footprint grew by four to five orders of magnitude. We did not ask whether we were getting four to five orders of magnitude of additional value. We assumed we were.
Sector Lisp answers the question. Sector Lisp is a modern Lisp implementation with garbage collection, written in 223 lines of assembly. Its footprint is 436 bytes. It runs the proof assistant that shipped with Lisp 1.5. The difference between gigabytes and 436 bytes is not just hardware improvement. It is the removal of everything added to function-based programming languages in the attempt to make the functional paradigm general-purpose. Sector Lisp does not allow re-assignment. It does exactly what the pure functional paradigm does, nothing more. The bloat was the generalization. The core was always small.
Type checking is the most visible symptom. We have spent decades elaborating type systems, adding annotations, and refining inference engines. The argument is that type checking catches errors before they reach production. Robert Smallshire has cited research suggesting type checking catches approximately 3 percent of real-world errors. Whether that number is exact or not, it raises a question we have not asked seriously: what did the other 97 percent cost us, and what were we doing before type checking existed?
Sketchpad, in 1963, pioneered the graphical user interface and object-oriented programming. VisiCalc transformed the personal computer into a business tool. Unix built the foundation the internet runs on. None of these were built with automated type checking. Their creators were working in notations suited to their domains — small enough to hold in one head, expressive enough to say what they meant directly. Type systems grew because languages grew because the problem domain kept expanding beyond what the original notation was designed for. With multiple small domain-specific notations, much of what the type system guards against would not exist in the first place.
This is not an argument against type checking. It is an argument that type checking is a symptom, not a solution. The solution is to stop forcing every problem into a notation designed for a different class of problems. Build or borrow a notation that fits. Small notations can be held in one head. Small notations do not require elaborate annotation to stay consistent. Small notations, as Turbo Pascal and VisiCalc and Unix V1 showed, can change the world in under 40 kilobytes.
We did not get here by making bad decisions. We made locally reasonable decisions without measuring their cumulative cost. Each addition to the functional paradigm made sense in isolation. But we kept adding and never subtracted, never benchmarked against the baseline, never asked what we were comparing against. The baseline was 20 kilobytes and a spreadsheet that changed the world. We owe it to ourselves to compare honestly.
See Also
Email: [ptcomputingsimplicity@gmail.com](mailto:ptcomputingsimplicity@gmail.com
Substack: paultarvydas.s. bstack.com
Videos: https://www. youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/65YZUh6J. q
Leanpub: https:. /leanpub.com/u/paul-tarvydas
Twitter: @paul_tarvydas
Bluesky: @paultarvydas.bsky.social
Mastodon: @paultarvydas
(earlier) Blog: guitarvydas.github.io
References: https://guitarvydas.github.io/2024/01/06/References.html
Paid subscriptions are a voluntary way to support this work.

