My love affair with the Scheme programming language began in the front seat of a 1976 Ford Mustang. I had just bought a copy of Abelson and Sussman’s Structure and Interpretation of Computer Programs, which uses Scheme as the vehicle for a course in computer science. On the way home from the bookstore I grabbed some lunch at the drive-thru window, and then I sat in the McDonald’s parking lot munching my fries and reading. I sat there all afternoon, beguiled by the sensuous elegance of this new language, a dialect of Lisp that seemed to be just what I’d been looking for all my life.
My infatuation with Scheme turned out better than most romances that begin in a parked car. Twenty-some years later I remain fondly devoted to the language, even if I haven’t always been faithful to it. Still, I’ve changed over the years, and so has the object of my affections—both of us, most likely, not for the better.
Scheme has just gone through quite a major change. The document that defines the language is known as the Revisedn Report on the Algorithmic Language Scheme, where n has taken values ranging from 2 through 5 in successive versions of the standard. A year ago, a proposal for the Revised6 Report was published, and the Scheme community was invited to comment and eventually vote on it. (I mentioned the draft standard in an earlier post.) After much discussion, the votes are now in: The R6RS standard has been adopted, 67 to 35.
I have mixed feelings about the outcome. The language has put on a lot of weight, as reflected in the bulk of the standard document. In 1978 the first Revised Report ran to 34 pages. The final draft of R6RS comes in four parts that total 186 pages. The additions and changes to the language arguably make it more mature and robust—but those are not necessarily qualities you seek in a romantic partner. (And now I promise to set aside this annoying teenage-romance metaphor.)
As I see it, the driving force behind many of the major changes in Scheme was Python envy. It’s not that Schemers think highly of the Python programming language or want to adopt its syntactic and semantic features; what they covet is the highly organized and enthusiastic community of Python programmers, who have created thousands of freely available, ready-to-run libraries and modules. If you want to do something with Fourier transforms or sparse matrices or regular expressions, someone has probably written a Python package that will save you a lot of work, and that package will probably install and run with little fuss in the Python system on your computer. Scheme also has its cadre of enthusiasts, who have written and generously shared lots of useful code. But don’t count on getting that code to work in any Scheme system other than the one it was intended for. There are dozens of incompatible interpreters and compilers.
The reason for this state of affairs is not just that Scheme programmers are a bunch of anarcho-individualists who can’t be bothered to conform to anyone else’s standards. From the outset, Scheme was a language lab, a medium for exploring new ideas in the expression and control of computational processes. Experiments with variant notations were the whole point. At the same time, of course, Scheme devotees also wanted the language to be a practical tool for everyday use. Those two goals are not easily reconciled.
Questions of compatibility and portability are exactly what standards are supposed to address, and many of the innovations in R6RS aim squarely at improving this situation. For example, Scheme has long had a “tower” of numeric data types, starting with integers at the bottom, then rational numbers, reals, and finally complex numbers; numbers can also be exact or inexact and they can be represented as “fixnums,” “flonums” or “bignums.” Supporting all of these variations is quite a challenge, and so earlier versions of the standard allowed implementors considerable leeway. R6RS insists that every Scheme must have the entire numeric tower.
While enforcing uniformity in some respects, however, the standard also breaks with the past in other areas. Most notably, Scheme has always been a case-insensitive language:
FOO were the same symbol. No more.
Apart from issues of program portability, it seems to me that the main thrust of the new standard is to make Scheme a more serious environment for software development—a programming language for grownups. There are facilities for precompiled libraries and for “namespace management.” Libraries have version numbers, and there’s a whole minilanguage just to parse those numbers. Another minilanguage handles errors and exceptional conditions.
Another addition is a
record data structure, with named fields. The idea is hardly a novelty, and you might think that every variation on it might have been tried by now, but the R6RS authors have assembled a marvelous Swiss Army Knife version like nothing else in this sector of the galaxy. R6RS records can be mutable or immutable; they have parents and protocols; they can be flagged as sealed, opaque or nongenerative. There are two entirely separate (and incompatible!) mechanisms for creating records, one based on macros and the other on procedures. Wow! It takes eight and a half pages to describe all this. A cynic might suggest that the hidden agenda of the report is to make the language so complicated that at most one implementor will succeed in building a compiler, thereby mooting all questions of portability.
The part of the new standard that I find most curiously out of character is the concept of a “top-level program.”
A Scheme program is invoked via a top-level program. Like a library, a top-level program contains imports, definitions and expressions, and specifies an entry point for execution. Thus a top-level program defines, via the transitive closure of the libraries it imports, a Scheme program.
Except for the hifalutin’ stuff about transitive closures, this idea should sound familiar and unremarkable to Pascal or C programmers; to many Schemers and other Lispers, however, it introduces an alien way of life. Speaking for myself, I almost never write a Lisp “program”—a monolithic body of code that gets encapsulated and launched as a free-standing application. Within the Lisp environment, I write a procedure definition, and then I evaluate or compile it, test it, debug it, run it on some arguments and get back some values. Then I write another procedure, and another. All this happens in the REPL—the read, eval, print loop. The process is incremental, iterative, exploratory; I don’t know where I’m going until I get there. After a while I’ve got a bunch of procedures, and they interact with one another in complicated ways, but they don’t constitute a “program”—certainly not a “top-level program.”
Nothing in the standard abolishes my kind of exploratory programming, and I don’t expect the REPL to disappear, but the authors of R6RS do seem to favor a different style of programming. Call it software engineering vs. hacking.
The process that led to ratification of the Revised6 Report was not painless. The authors and editors labored mightily, and so did the critics. The procedure took twice as long as expected. There were intermediate drafts numbered R5.91, R5.92, R5.93, R5.94, R5.95, R5.96 and R5.97, in some cases reflecting substantial changes. My archive of the discussion list has 3,276 messages (I may have missed a few, and I haven’t read them all) and amounts to 14.3 megabytes. Some of the controversies that erupted on the mailing list could not have been avoided: The Lisp community has been feuding for generations over the semantics of macros and the morality of an
eval procedure. There were plenty of other disputes as well. The usual law of committee action was in effect: Time spent on an issue is inversely proportional to its importance. (We had quite a spirited debate over whether 0.0 = –0.0. The final draft punts on this issue.)
In an unusual voting procedure, those who opposed ratification were required to state their reasons; some of those casting votes in favor also appended explanations. (You can read all the statements here.) The dissenters’ reasons vary, and I hesitate to summarize, but one theme comes up so often that it bears mentioning here. Many of the no-voters complain that the new standard repudiates a philosophy of minimalism that guided the early growth of Scheme. The principle was stated eloquently by Will Clinger in a preamble that has led off all the versions of the Scheme standard since R3RS:
Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary. Scheme demonstrates that a very small number of rules for forming expressions, with no restrictions on how they are composed, suffice to form a practical and efficient programming language that is flexible enough to support most of the major programming paradigms in use today.
Almost a third of the nay votes allude to this passage. One of those votes comes from Clinger himself.
As for me, I didn’t vote. This wasn’t a principled abstention; I simply missed the deadline. I do have an opinion. I’m not convinced that R6RS is an improvement over R5RS. And I’m pretty sure the new language is not going to be as much fun in the front seat of a ’76 Mustang.