No language is free
Every programming language is a set of decisions, and almost every decision costs something. A language that makes common tasks short usually makes unusual tasks awkward. A language that catches many mistakes before a program runs usually asks the programmer to write more up front. There is rarely a free improvement, only a trade.
Mainstream languages hide this because their designers try to balance many goals at once. Esoteric programming languages are useful for studying design because their creators often pick one goal and push it far past the point of comfort. Seen that way, they work like laboratory experiments: they show what happens to a language when one dial is turned all the way up.
Simplicity versus convenience
The most obvious dial is size. A small language is easier to describe, easier to implement, and easier to hold in your head. A large language gives you more ready-made tools. The two pull in opposite directions.
The Brainfuck commands are a famous example of the small end: eight single-character operations on a row of memory cells. A complete interpreter fits in a short program. The price is that even simple tasks, such as adding two numbers, turn into long strings of symbols. The language is easy to implement and exhausting to use. This pattern, where a small core pushes work onto the programmer, comes up again in how few commands a language needs, and it is the central lesson of minimalism.
Readability versus density
Designers also choose between code that is quick to write and code that is quick to understand. Dense notation lets an expert say a lot in a line, but a newcomer has to decode it. Spelled-out notation is friendlier but longer.
Esoteric languages explore both extremes. Some are so compact that a program looks like noise, which is part of why code readability is such a recurring complaint. Others change the surface entirely: the Whitespace language uses only spaces, tabs and line breaks as meaningful characters, so a program can look blank on screen. Both choices are deliberate and both make reading hard, for opposite reasons.
Where the program lives in space
A language also decides what shape a program has. Most languages treat source code as a line-by-line sequence of text. The Befunge language instead places instructions on a two-dimensional grid, and an instruction pointer moves across it in changing directions. That choice makes control flow something you can see as a path, and it also makes some ordinary things, such as writing a long straight-line routine, awkward.
Taking this further, Piet colors replace text altogether with blocks of color, so the program looks like abstract art. The tradeoff is clear: a visual form can be striking and can make structure obvious, but it is hard to type, search, compare and edit with ordinary tools.
Memory and data
How a language stores data shapes everything built on it. A stack gives you simple, fast access to the most recent value and nothing else. A tape lets you move back and forth but one cell at a time. A grid lets code and data share space. Each of these memory models makes some programs short and others painful, and none is best at everything.
Stack machines are attractive because their instructions need almost no addressing: an addition simply uses the top two values. The cost is that you must keep track of the order of values in your head, and a small mistake can quietly shift everything. That bookkeeping burden is one of the first things beginners notice.
Safety versus freedom
A designer also decides how much the language protects the programmer. Some languages check types, bounds and memory use and refuse to run programs that look wrong. Others allow nearly anything.
Many esoteric languages take the permissive side, often because checking would add rules and the goal is to keep the rules few. A Befunge program can overwrite its own instructions while running, which is powerful and also very easy to get wrong. A language with strict checking prevents a class of mistakes, but it also rules out some clever tricks. Neither side is simply right; it depends on who the language is for and what they are trying to do.
Predictability versus power
Features that interact in unexpected ways make a language harder to reason about. A language with a few features that combine cleanly is easier to predict than one with many special cases. That idea is explored in more detail in the discussion of orthogonal design. Power points the other way: adding features usually lets programmers do more, even if the interactions become harder to track.
Being useful versus being interesting
Not every language is trying to be practical. Some are built as jokes, some as puzzles, and some as proofs that a very small system can compute. A language that is intentionally difficult makes a different set of tradeoffs from one meant to ship real software, and judging it by practical standards misses the point. When a designer sets out to make programming strange on purpose, the “cost” of unreadability becomes the product.
This is also why esoteric languages have real learning value. Because they strip away or exaggerate features, they let you see each tradeoff in isolation. After writing a few small programs in one, it becomes easier to notice the same tensions in a mainstream language that hides them behind conveniences.
How to think about a design choice
When you meet a language feature, whether in an esoteric language or a mainstream one, a few questions usually help:
- What does this feature make easy, and what does it make harder?
- Who is the language for, and does this choice suit them?
- What does the feature interact with, and could that cause surprises?
- Is the cost paid by the person writing the code, the person reading it, or the person implementing the language?
Asking these turns a language from a list of rules into a set of reasoned, and sometimes debatable, choices. Reasonable designers disagree about many of them, and the disagreement is a large part of why so many different languages exist.