The Tragedy of the Common Lisp:
Why Large Languages Explode
Adapted from a 2015 es-discuss thread. “Common Lisp” is not the topic. It serves only as one of many illustrative counter-examples.
Each of the additions that grew EcmaScript-5 to EcmaScript-2015 had to pass a very high bar. Psychologically, this made sense to all of us because we were starting from a language, EcmaScript-5, whose smallness we could still appreciate. When a language is small, every additional feature is viscerally felt as a significant percentage increase in the size of the language. The specific benefits of a feature are always visible to its advocates. For a small language, a new feature’s general costs in added complexity are also still visible to everyone.
Once a language gets beyond a certain complexity — say LaTeX, Common Lisp, C++, PL/1, modern Java — the experience of programming in it is more like carving out a subset of features for one’s personal use out of what seems like an infinite sea of features, most of which we become resigned to never learning. Once a language feels infinite, the specific benefits of a new feature are still apparent. But the general costs in added complexity are no longer apparent. They are no longer felt by those discussing the new feature. Infinity + 1 === Infinity. Even aLargeNumber + 1 === approximatelyAsLargeANumber. This is the death of a thousand cuts that causes these monstrosities to grow without bound.
So please, I beg everyone influencing the language, when considering a new feature, please apply a higher bar than “Wouldn’t it be nice if we could also write it this way?”. I believe that EcmaScript-2015 is in that middle territory where unrestrained growth is not yet inevitable, but only if we all restrain each other with high standards for any proposed new feature. As a community, we need more of a shared sense of panic about the size that EcmaScript-2015 has already grown to. Ideally, our panic should increase, not decrease, with further growth from here as our size approaches the point of no return.
The urgency of minimalism gets weaker as we move from core language to standardizing libraries. The overall standard language can be seen as consisting of these major parts:
- Fundamental syntax — the special forms that cannot faithfully be explained by local expansion to other syntax.
- Semantic state — the state that computation manipulates.
- Kernel builtins — the portion of the built in library providing functionality that, if it were absent, could not be provided instead by user code.
- Syntactic sugar — the syntax that can be explained by local expansion to fundamental syntax.
- Global convenience libraries — could be implemented by unprivileged user code, but given standard global naming paths in the primordial global namespace.
- Standard convenience modules — blessing community-developed modules as standard.
I have listed these in order, according to my sense of the costs of growth and the urgency for minimalism. For all of these we still need to exercise discipline. Only for the last one that we should consider growth of absolute size to be unbounded; restricting only the rate of growth as we wait for candidates to prove themselves first by the de facto process of community adoption. Ideally, TC39 should stop being the bottleneck on the last bullet anyway, as external de facto and de jure processes should be perfectly capable of independently arguing about and evolving standard convenience modules.