We Painted Ourselves Into a Corner
2026-06-03
TL;DR Synchronous, sequential notation has colonized all of modern programming. Every mainstream language — Python, JavaScript, Haskell, Rust, Clojure — is the same at the core: sync. Once you commit to sync, asynchrony is not just inconvenient to recover; it is structurally unrecoverable. We have compounded this by treating analysis-oriented notation as if it were expression-oriented notation. The result is that we are solving 21st-century problems with 20th-century tools, pasting bandaids on bandaids, and calling it progress.
L;R
Python, JavaScript, Haskell, Rust, Clojure. These languages look different. They feel different. Their communities are different. But they are the same at the core: every one of them is built on the synchronous, sequential paradigm. This paradigm is not a feature of these languages — it is the water they swim in. It permeates every language decision, every library design, every idiom. The differences between these languages are lipstick on the same pig.
The mechanism is simple. As soon as you write a function that calls another function, you have committed to synchronous control flow. The callee runs. The caller waits. Time is implicit. Order is enforced. On paper — where the laws of physics do not apply to notation — this works fine as a simplification. But when you map this onto real, physical hardware, the implicit assumptions start leaking. The hardware does not wait. The network does not wait. The user does not wait. And no amount of async/await sugar, thread libraries, or reactive frameworks recovers what was lost when you wrote that first function call. You cannot put asynchrony back into a notation that has already committed it away.
This is not a theoretical complaint. It shows up as “callback hell.” It shows up as cache coherence problems and thread-safety rituals. It shows up as the entire edifice of concurrency primitives — semaphores, mutexes, futures, promises — which exist not because concurrency is complicated, but because we chose a notation that cannot express it directly, and then spent decades building scaffolding to work around that choice. We painted ourselves into a corner and responded by building more elaborate furniture to stand on.
A five-year-old taking piano lessons understands something that a senior software engineer, working in a modern language, cannot directly express. The child learns to get the left hand and the right hand to play different notes at the same time — not sequentially, not taking turns, but concurrently, each following its own rhythm. The child learns hard realtime: notes must land at exactly the right moment. The child learns a notation — standard musical notation — that captures all of this succinctly. Duration, timing, concurrency, relative ordering: one page of sheet music expresses what no mainstream programming language can express without significant ceremony. The five-year-old’s intuition is a tell. The problem is not complicated. Our notation made it complicated.
We have not only chosen a paradigm poorly suited to concurrency. We have allowed the demands of analysis to colonize the demands of expression. These are different things. A notation suited to analyzing a concept is not necessarily suited to expressing that concept in a running system, and vice versa. David Harel’s Statecharts are the clearest demonstration I know of how analysis can destroy the very thing it sets out to formalize. Harel’s original statechart paper gave us a notation that expressed asynchronous parallelism directly. States, transitions, hierarchy, concurrency — all visible, all expressible in a diagram. His follow-up micro-semantics paper formalized the concept analytically. In doing so, it forced sequentiality onto the model. The asynchronous parallelism of Statecharts was converted into synchronous concurrency. The formalization was correct analytically and useless practically. I discovered this early on, trying to use the formalized version in a real, live, asynchronous system. The analysis had eaten the property I needed.
The same pattern appears in pBFT — Castro and Liskov’s 1999 solution to the Byzantine Generals Problem, which influenced several blockchain implementations.. The concept at the heart of pBFT is actually two concepts: cryptographic constructs for encryption, and state-machine constructs for the ebb and flow of node participation, drop-in, and drop-out. The cryptographic part is naturally expressed in mathematical, functional notation. The state-machine part is naturally expressed as a state machine — or better, a Statechart. But the paper was written in one notation. The state-machine behavior was forced into the same synchronous sequential framework as the encryption. The extra work required to do this was not free. The brain power spent on the impedance mismatch was brain power not spent on other problems.
A simpler example: I built a three-state state machine and expressed it as a diagram. The same machine, transcribed into Python, runs to approximately sixty lines of code. A seasoned Python programmer could trim that number. But no amount of trimming recovers what the diagram gives for free: the visual structure of the states, the transitions, the conditions, all immediately apparent to anyone who looks. The Python version is not wrong. It is a different notation, and it obfuscates the information that the diagram expresses directly. We use the wrong notation and then spend effort reconstructing what a better notation would have given us at no cost.
The same principle, this time with a textual notation: Forth Haiku. Two lines of Forth Haiku control a GPU as well as a hundred lines of GLSL and JavaScript. This is not a party trick. It is a demonstration that a notation suited to its domain can be radically more expressive than a general-purpose notation applied to the same domain. Critically, generalizing Forth Haiku — trying to make it do everything — would produce a mess. Exponential growth of code. The mess is a tell. The right lesson is not to generalize Forth Haiku. The right lesson is that domain-specific notation, applied with discipline, beats general-purpose notation applied with force.
Casey Muratori and the Handmade Network have been making a version of this argument from the direction of games programming: our current programming languages are not well suited to building games. The gaming industry’s response has been to work around the languages, not with them. This is the same pattern everywhere. The workarounds are not solutions. They are evidence that the foundation is wrong.
We tell ourselves we are standing on the shoulders of giants. In many cases we are. But the giants were solving single-CPU, synchronous, sequential problems. Their shoulders point in one direction. The problems of the 21st century — distributed systems, asynchronous networks, multi-core concurrency, real-time interaction — are in a different direction entirely. Continuing to build on those shoulders, in those notations, is not progress. It is the definition of doing the same thing over and over and expecting different results.
The prescription is not a new general-purpose language. The prescription is to stop insisting on one. Use synchronous notation where synchronous notation fits: mathematical expression, encryption, pure function. Use state-based notation where state-based notation fits: control flow, lifecycle, protocol. Use diagram-based notation where diagrams carry information that text cannot. Use whatever notation is suited to the part of the problem you are solving. Match the notation to the domain, not the domain to the notation. Build a small, cheap, custom notation when none of the existing ones fit. OhmJS and PEG parsers make this as accessible as writing a regular expression.
We have the tools. We have the intuition — five-year-olds have it. What we are missing is the permission to stop treating one notation as the answer to every question.
References
Statecharts PWL video
microsemantics of Statecharts
Muratori “The Big OOPs - Anatomy of a Thirty-Five Year Mistake”
Forth Haiku video playlist
3 state state machine video (convert state machine diagram to Python)
See Also
Email: ptcomputingsimplicity@gmail.com
Substack: paultarvydas.substack.com
Videos: https://www.youtube.com/@programmingsimplicity2980
Discord: https://discord.gg/65YZUh6Jpq
Leanpub: [WIP] 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.

