Stop Confusing Languages With Compilers
2026-07-17
Most programmers treat “language” and “compiler” as the same thing. Say “I built a new language” and people hear “I built a new compiler” — parser, type checker, code generator, the works. Months of effort, before you’ve written a single line in the thing.
I don’t think that’s right, and untangling the two ideas changes what’s possible.
A language is just a notation. It’s a way of writing down a problem so you can think about it more clearly — nothing more mystical than that. A compiler is a separate piece of machinery: code that takes a notation and turns it into instructions a CPU can run. We’ve bundled them together for so long that it’s easy to forget they’re doing different jobs. One is about human thought. The other is about machine execution.
Once you pull them apart, a question follows naturally: can you build a new language in a fraction of the time it takes to build a language and a full compiler for it? I think the answer is yes, and it’s yes by a wide margin. There are a few ways to get there.
Markdown for code
The first way is to treat a new language the way markdown treats formatted text: a thin, readable notation that gets translated into something else, rather than compiled from scratch down to machine code. That’s what I was exploring in Separating Architecture From Code and Implementing The Experimental ST Meta-Syntax, using a tool called T2T. The new language isn’t compiled in the traditional sense — it’s transformed. You get the benefit of a purpose-built notation without paying the traditional cost of a purpose-built compiler.
There’s a second idea worth pulling out of that markdown comparison, beyond just “lightweight notation.” Markdown lets you nest headings — #, ##, ### — so a document can be collapsed down to its top-level outline or expanded to full detail, depending on what the reader needs. I think code notations can do the same thing: present a system in layers that can be elided or drilled into, rather than as one flat wall of implementation. That has real consequences for onboarding. Instead of a new team member reading an entire codebase to understand it, they could read the top layer to get the architecture, then descend a layer at a time into whatever part they actually need to touch. People could understand a given architecture and its implementation down to whatever level of detail they care to delve into, and no further.
Explain it to an LLM
The second way is more recent, and honestly a bit strange to say out loud: describe your new language to an LLM, in plain language, and have it generate code from any source written in that notation. No parser to write, no grammar to formalize up front, no code generator to hand-tune. You explain the rules the way you’d explain them to a new hire, and the model does the translation.
To my understanding, this is roughly what Steve Phillips did with Voltair, a new language he’s been designing. At least in the early stages, he had Voltair source transpiled into Go rather than building a traditional compiler for it first — letting the language’s design settle before investing in dedicated compiler machinery.
This is where the language/compiler split really pays off, and it pays off regardless of which route you take. Whether you’re using something like T2T to transform a notation into another form, or an LLM that’s been told how your notation works, the cost of inventing a new language drops from months to hours.
Nobody has to like the target language
Here’s the part that trips people up. A lot of programmers dislike JavaScript for the same reasons they dislike assembler or C — verbose, error-prone, full of sharp edges. That dislike is real, and it’s earned. But it stops mattering the moment a machine, not a human, is the one writing the JavaScript.
We tend to judge languages by a single standard: does it prevent and catch the kinds of bugs humans make? That’s exactly the right standard when a human is going to sit down and type the code. It’s close to irrelevant when a machine is generating it. A “good” target language for generated code has a completely different set of criteria: syntax that’s easy to generate mechanically, and enough semantic flexibility that you’re not fighting the language’s opinions — GOTOs included, if that’s what the generator needs to produce. That last point actually narrows the field: neither JavaScript nor WASM supports raw GOTOs, while C and assembler do, since both are built around structured control flow rather than arbitrary jumps. That’s a real constraint on which target language makes sense for a given generator. Sometimes the deciding factor is even more mundane: you need to hook into something that only speaks a specific language. OhmJS, for instance, requires its semantic actions to be written in JavaScript (WASM support has recently arrived). If your generated code needs to talk to OhmJS, JavaScript stops being a matter of taste and becomes a matter of plumbing.
Once you separate “language good for humans to write” from “language good for machines to generate,” the reflexive dislike of JS or C or assembler just stops being relevant to half the conversation.
Little languages, on purpose
Little languages exist to make it easier for humans to work out solutions to specific problems — that’s the whole point of a domain-specific notation. If we accept that generating a little language is now cheap, it opens the door to a different way of building software altogether.
I call these project-specific notations SCNs — Solution Specific Notations — and I think a single project can reasonably contain many of them, each shaped around a particular slice of the problem rather than one general-purpose language stretched to cover everything.
The usual objection is that designing and implementing a language is hard — genuinely, historically hard. But look at where that difficulty actually comes from. From the language-builder’s side, yes, compilers are complicated machines. But from the language-user’s side, the difficulty is different: it’s that learning a new, general-purpose language tends to mean learning something big and overly complicated, because general-purpose languages have to cover every use case anyone might ever have. If the languages we asked people to learn weren’t so sprawling and so generalized — if each one only had to cover the one small problem it was built for — we might be a lot more willing to pick up a new little language every time we needed one. That objection made complete sense as long as “new language” meant “new compiler” and “compiler” meant “months of general-purpose language design.” But if a plethora of small, specialized, low-cost languages is actually within reach — sketched out with something like T2T, or handed to an LLM with a short explanation — then the complexity argument stops being a wall and starts being a leftover assumption from a different era of tooling.
That’s the shift I’m interested in: not one language to rule a whole project, but as many small ones as the problem actually calls for, each one cheap enough that inventing it isn’t a decision you have to agonize over.
Appendix - T2T
T2T is one of the tools in the PBP toolkit — once you install PBP, you have access to T2T.
Appendix RWR
RWR is part of T2T. This is the specification for RWR
Appendix - PBP
Appendix - Further About PBP
Towards Parts Based Programming
PBP cookbook playlist
Decision Tree Diagram Transmogrifier
State Machine Diagram Tool
FDD LLM - 5 Whys Tool - code repository
State Machine Tool - code repository
Decision Tree Tool - code repository
PBP all tools (PBP, T2T, das2json)
The Spherical Cows of Programming
The Wrong Spherical Cow - First Principles of Parts Based Programming
The Case For Composable Notations (1 / 5) - The Spherical Cow We Forgot We Were Riding
The Case For Composable Notations (2 / 5) - The Restrictions That Came With The Cow
The Case For Composable Notations (3 / 5) - State Isn’t The Enemy The Case For Composable Notations (4 / 5) - Ease of Expression Is the Whole Point
The Case For Composable Notations (5 / 5) - UNIX Already Showed Us the Way
The Case For Composable Notations (6 / 5) - WIP - Notations in Progress
See Also
Email: 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.

