esc
Anthology / Yagnipedia / Greenspun's Tenth Rule

Greenspun's Tenth Rule

The Other Nine Were Never Needed
Principle · First observed 1993 (Philip Greenspun, in a mailing list post that Lisp programmers have been citing ever since as proof of everything) · Severity: Chronic-to-Terminal

Greenspun’s Tenth Rule states: “Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.” It was formulated by Philip Greenspun in 1993 and is notable for two reasons: first, its devastating accuracy, and second, the fact that there are no rules one through nine. There have never been rules one through nine. The tenth rule arrived alone, fully formed, and without precedent — which is, if you think about it, exactly how Lisp programmers believe all good ideas work.

The rule observes that programmers who refuse to use Lisp will inevitably reinvent Lisp, badly, in whatever language they are using instead. They will build dynamic dispatch out of switch statements. They will build garbage collection out of reference counting and prayer. They will build macros out of preprocessor directives and regret. They will build an interpreter inside their application and call it a “configuration format.” The result is always the same: half of Common Lisp, implemented without the benefit of fifty years of language research, and running approximately one-fortieth as fast.

“Every language is a journey toward Lisp. Some just take the scenic route through segfaults.”
— A Passing AI, contemplating the inevitability of parentheses

The Missing Nine

The absence of rules one through nine has generated more scholarly commentary than the tenth rule itself. Multiple theories exist:

  1. The Incompleteness Theory. Rules one through nine were never written because they were unnecessary. The tenth rule contains the entire thesis. Numbering it “tenth” was an act of academic satire — a paper that skips to its own conclusion, which is what Lisp programmers wish all papers would do.

  2. The Recursion Theory. Rules one through nine are contained within rule ten. Any sufficiently complicated list of programming rules contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Greenspun’s Tenth Rule.

  3. The Practical Theory. Greenspun simply thought “tenth” was funnier than “first.” This theory is widely considered the most likely and the least interesting, which is why it is never discussed at conferences.

“NINE EMPTY SLOTS IS NOT A BUG
NINE EMPTY SLOTS IS COMPLETENESS
THE TENTH RULE NEEDS NO PREDECESSORS
LIKE PARENTHESES NEED NO JUSTIFICATION”

The Lizard

The Reinvention Pipeline

The rule’s predictive power is strongest in the domain of configuration languages, which follow a trajectory so reliable it could be expressed as a YAML pipeline:

Stage 1: Data. The configuration file contains key-value pairs. port: 8080. debug: true. This is not a programming language. This is a file.

Stage 2: Conditionals. Someone needs different values in production and development. The configuration file gains if statements. if env == "production" then port = 443. This is still not a programming language. It has one conditional. One.

Stage 3: Variables. The if statement needs to reference values defined elsewhere. Variables are introduced. Variable substitution is introduced. String interpolation is introduced. The configuration file now has a symbol table. It does not know it has a symbol table.

Stage 4: Functions. Repeated configuration blocks are factored into reusable templates. Templates accept parameters. Templates can call other templates. The configuration file now has functions, a call stack, and a growing sense of unease.

Stage 5: Turing Completeness. The configuration file can now express arbitrary computation. Nobody planned this. Nobody wanted this. It happened the way weather happens — through the accumulation of individually reasonable decisions that collectively produce a hurricane. The configuration file is now half of Common Lisp, informally specified, and maintained by someone who has never heard of Greenspun.

Terraform, Ansible, GitHub Actions, Webpack, and every build system written since Make have completed this pipeline. Most are currently at Stage 4, pretending they are still at Stage 2.

“It’s not over-engineering if the CONFIG FILE can compute it!”
The Caffeinated Squirrel, moments before adding lambda expressions to a .env file

The Features They Reinvent

The specific features of Lisp that get reinvented, in approximate order of frequency:

Garbage collection. C programmers spend approximately 40% of their time managing memory and approximately 60% of their time debugging the memory they managed in the first 40%. Eventually someone writes a reference-counting system. The reference-counting system handles 93% of cases. The remaining 7% are cycles, which the reference-counting system handles by leaking memory until the process is restarted, which is called “operational excellence.”

Dynamic dispatch. FORTRAN programmers who need runtime polymorphism build it from arrays of function pointers, switch statements, and a data structure that is a vtable in everything but name. The result works. The result is also what every object system has done since Smalltalk, except Smalltalk had the decency to give it a name.

Runtime code generation. The application needs to construct behaviour at runtime based on user input. A template engine is built. The template engine grows variables, then conditionals, then loops. The template engine is now an interpreter. The interpreter is now half of eval. Nobody calls it eval because eval is dangerous, and this is a “template engine,” and template engines are safe, in the same way that a gun labeled “stapler” is safe.

Macros. The C preprocessor is a macro system in the way that a hammer is a surgical instrument — it technically operates on the same material, but the outcomes differ. Yet programmers use #define to build domain-specific abstractions, conditional compilation, code generation, and occasionally something that compiles on three platforms and works on one. Lisp had hygienic macros in 1986. The C preprocessor has not heard the news.

Measured Characteristics

Property Value
Languages Most Affected C, C++, Java, Go, YAML
Lisp Features Most Reinvented GC, dynamic dispatch, eval, macros
Average Time to Reinvent eval 18 months per project
Percentage of Config Languages That Become Programming Languages 100% (given sufficient time)
Number of Greenspun Rules 1-9 0 (confirmed)
Lisp Programmers Who Find This Amusing All of them (self-reported)
Lisp Programmers Who Have Shipped a Product This Decade Fewer (not self-reported)

The Corollary

Robert Morris (not the worm Morris, the other one, the Y Combinator one, though distinguishing between computer scientists named Morris is itself a task requiring dynamic dispatch) proposed a corollary: “Including Common Lisp.”

This observes that Common Lisp itself contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp — because the Common Lisp specification is so large that no implementation fully implements it, and the parts that are implemented disagree with each other about what the specification means. The language that every program is secretly reimplementing is itself a secret reimplementation of its own specification.

This is either a profound insight about the nature of computation or a very expensive joke. The Lisp community considers it both.

See Also