In 1991, Richard P. Gabriel, founder of the Lisp company Lucid, published an essay on why C and Unix had caught on while Lisp had not. Its most famous section illustrates the argument with one small design decision: what an operating system should do when a program is interrupted in the middle of a system call. Unix left the retry to the program, an approach Gabriel called “worse is better.”
Gabriel told the story as a meeting between two famous people, “one from MIT and another from Berkeley (but working on Unix).” The essay does not name them. The MIT man knew ITS, the operating system of the Artificial Intelligence Lab at the Massachusetts Institute of Technology, and had been reading the Unix source code. He wanted to know how Unix solved the PC loser-ing problem.
The problem comes up when a program asks the operating system for a lengthy operation that holds significant state, such as input/output buffers, and an interrupt arrives partway through. The request is usually a single instruction, so the program counter — the PC, which records where the program is — cannot capture how far the system routine has got. The name “PC loser-ing” was an MIT joke: “loser” was the affectionate word there for a user. The system routine, Gabriel wrote, “must either back out or press forward.”
Three ways to handle an interrupted system call
The answer Gabriel called “the right thing” was to back out: restore the PC to the instruction that made the call, so the program simply enters the routine again when it resumes. ITS, the Incompatible Timesharing System, was built so that a program never saw a system call half done. In Alan Bawden’s description of the technique, a call interrupted partway through updated its own arguments in place, so the restart continued where it had stopped. “System calls thus behave as if they were directly implemented in hardware,” he wrote.
Unix took a simpler route. In Gabriel’s telling, the system routine always finished, but sometimes it returned an error code saying it had failed to complete its action. A correct program had to check that code and decide whether to try again. On AT&T’s System V and on 4.1BSD, a release of Berkeley’s Unix, that code was EINTR: a call cut off before it had transferred any data returned −1 with that error.
Each path had a price:
- Back out, as ITS did: programs never see a half-finished call, but the operating system must know how to undo or resume every long one. Writing later under a pen name, Gabriel put the code for that solution at “a hundred or so pages of assembly language.”
- Finish and report, as Unix did: the system side is “hardly anything at all,” by the same account, but every correct program needs a test and a loop.
- Restart automatically, as 4.2BSD later did: a middle path. Berkeley made restarting the default, but only for a call interrupted before any data had moved.
Why the Unix side called the simple version right
The Unix man did not deny the cost. Gabriel calls him “the New Jersey guy,” after Bell Labs in New Jersey, where Unix and C were developed. The Unix folks were aware of the problem, the New Jersey guy said, but the design philosophy of Unix was simplicity, and the right thing was too complex. “Besides, programmers could easily insert this extra test and loop.” When the MIT man objected that the interface was now complex, the answer was that implementation simplicity mattered more than interface simplicity.
Gabriel had set up the story with two lists of the same four values — simplicity, correctness, consistency and completeness — weighed differently. The right thing ranks a simple interface above a simple implementation and allows no incorrectness. Worse is better puts implementation simplicity first: “It is slightly better to be simple than correct.” A worse-is-better design must still be “correct in all observable aspects,” but completeness can be sacrificed for any other quality.
His conclusion was an odd one for a Lisp vendor. Worse is better, “even in its strawman form, has better survival characteristics than the-right-thing.” Putting implementation simplicity first, he argued, made Unix and C easy to port. Software like that spreads, and once people are hooked, it can be improved toward 90 percent of the right thing. “Unix and C are the ultimate computer viruses,” he wrote.
The bill: a retry loop that outlived the argument
On Linux today, many blocking calls interrupted by a signal handler restart automatically only if the handler was installed with the SA_RESTART flag; otherwise, in the manual’s words, “the call fails with the error EINTR.” Some calls, such as poll and select, are never restarted, whatever the flag says.
Before version 3.5, Python code that had to survive interruptions caught the error at each call site. In the last versions before 3.5, a typical loop looked like this:
while True:
try:
data = file.read(size)
break
except InterruptedError:
continue
PEP 475, a 2014 Python Enhancement Proposal by Charles-François Natali and Victor Stinner, moved that loop into Python itself. Its argument was the MIT man’s complaint in modern form: “Without special support from Python, this can make application code much more verbose than it needs to be.”
Gabriel argued the other side, too
In “Worse Is Better Is Worse,” signed Nickieben Bourbaki and presented as the work of a long-time friend, Gabriel wrote that “Richard was dangerously wrong to publish that paper.” By that account, Gabriel had coined the slogan in spring 1989, apparently on the spot, while friends chatted about why Lisp had not caught on.
Yet the reply also makes a strong case for the Unix decision. The right-thing code was so complex “that many consider it nearly impossible to get it right.” And, the reply argues, there was never a head-to-head competition: Unix ran on Digital Equipment Corporation’s PDP-11 computers, while “the other, better operating systems that Richard is talking about were on the PDP-10 which did not compete with the PDP-11.” MIT’s Jerome Saltzer, in a teaching guide to Gabriel’s essay, goes further: he advises skipping the PC loser-ing passage and argues that the two men in it are “talking past each other.”
The decision rule: who writes the extra loop?
The PC loser-ing decision did not remove work. It moved the work out of one kernel routine and into every program that made the call. Sometimes that is the right trade: a small kernel was easier to write and to port, and that, Gabriel argued, is how Unix spread. But the bill is paid per caller, for as long as the interface lives.
So before you simplify your side of an interface, the useful question is: who will now write the extra test and loop, and how many times? For Python, the answer came with version 3.5, which moved into the language itself the loop that the New Jersey guy had called easy to insert.
Related stories
Sources
- Richard P. Gabriel, “The Rise of ‘Worse is Better’,” excerpt from “Lisp: Good News, Bad News, How to Win Big,” AI Expert, June 1991 (MIT course copy)
- Nickieben Bourbaki (Richard P. Gabriel), “Worse Is Better Is Worse,” copy dated December 4, 2000 (Princeton course copy)
- Alan Bawden, “PCLSRing: Keeping Process State Modular” (copy)
- J. H. Saltzer, teaching guide to Gabriel’s “Lisp: Good News, Bad News, How to Win Big,” MIT
- Linux man-pages project, signal(7): interruption of system calls and library functions by signal handlers
- BSD manual page siginterrupt(3) (Apple developer archive)
- Charles-François Natali and Victor Stinner, PEP 475, “Retry system calls failing with EINTR,” 2014
- Wikipedia, “Worse is better”
- Wikipedia, “Lucid Inc.”
- Featured image (illustrative, not a machine from this story): DECSYSTEM-10 KL10 2065 with part of a PDP-11 at left, Living Computer Museum, Seattle, by Joe Mabel, via Wikimedia Commons, CC BY-SA 3.0

Leave a Reply