Edited by Peter Salus

Review by Glenn Vanderburg

Originally published in ;login:, the magazine of the USENIX Association.

The Handbook of Programming Languages---which I will refer to as the Handbook---is by any measure an ambitious project. Four volumes, about two thousand pages, 50 languages (if you count the really little ones), 49 authors (many of them the designers of the languages in question), or almost $200---whatever your metric, it's a big project. Reviewing it is no small task, either. After a quick overview of the set as a whole, this review discusses each of the volumes in turn and concludes with some comments that apply to all of the books.

The volumes are divided roughly along the lines of different kinds of languages:

  • Volume I: Object-Oriented Programming Languages
  • Volume II: Imperative Programming Languages
  • Volume III: Little Languages and Tools
  • Volume IV: Functional and Logic Programming Languages

I was surprised a few times by where particular languages appear in this lineup, but for the most part the divisions make sense.

I believe a book review should tell readers why they should or should not buy a book. In this case that's an unusually complex and slippery question. When Salus announced the publication of the Handbook in ;login:, he said "Ask your corporate or University library to buy a copy." I think that most libraries used by programmers should have a copy of this work, so Salus' recommendation is a good one. However, some individuals may want to purchase one or more of these volumes. Depending on your needs, these books may or may not be worth buying.

Why would you want to have four big books about a bunch of programming languages? I can think of four reasons:

  1. You could be a strange aficionado of programming languages like I am. If so, the Handbook is almost inevitably going to end up on your bookshelf.
  2. You might want a quick introduction to languages you might encounter in your work (if, for example, you are occasionally called upon to maintain or enhance---or merely decipher---existing programs). The Handbook is adequate for this need, but the curious omission of some widely used languages, most notably COBOL, will be frustrating.
  3. You might want a survey of languages to help you choose the right language for your development projects. If you fit this description, these books are particularly well suited for you, since most of the current languages that are anywhere close to the mainstream are covered in detail. Even though the authors are closely associated with the languages they write about, nearly all of them have kept their advocacy to reasonable levels, so fair evaluations of the languages are possible.
  4. Finally, you might want to learn about different ways of thinking about problems and expressing their solutions, in the hope that this might make you a more creative problem solver or have some similar benefit. For you, the Handbook will be a feast that ends too soon, before you get to some of the really mind-stretching languages.

In reading these books, I looked for several kinds of information about each language:

  • historical relationships with other languages
  • a concise statement of the design philosophy
  • a quick summary of distinguishing characteristics
  • enough details to permit reading most programs easily
  • examples and discussion that give a good taste of the feel of programming in the language---coding idioms, styles of problem solving, overall program structure

In most cases I was pleased by what I found. I did not consider exhaustive syntactic or semantic detail to be particularly important; these books make no claim to be complete language references.

Volume I: Object-Oriented Programming Languages

Volume I begins with an essay on object-oriented programming and a paper about the application of design patterns and frameworks. The introductory essay is by Timothy Budd, and it's hard to think of a better choice. In just a few pages, Budd provides a well-written, easy but thorough overview of object-oriented programming. In keeping with his excellent text on the topic, he manages to keep the discussion free of the details of particular languages, mentioning three (Simula, Smalltalk, and C++) solely to provide historical context. Doug Schmidt's paper about patterns is also good, but focuses too much on a single application domain (network communication software).

Six object-oriented languages are covered in this volume: Smalltalk, C++, Eiffel, Ada95, Modula-3, and Java. Smalltalk, C++, and Java each merit three chapters, the others only one apiece. With the exception of Ada95, the designers of these languages have made substantial contributions to the book.

The Smalltalk and C++ sections are particularly strong, with excellent coverage of not only the languages themselves, but also the histories and user communities. Bjarne Stroustrup has contributed his usual thorough history and self-critique of C++. My biggest complaint about these chapters is that one of Smalltalk's noted strengths, GUI development, is barely mentioned.

Bertrand Meyer's chapter on Eiffel successfully explains Eiffel's design as a part of the overall software lifecycle. The bulk of the chapter, however, spends too much time on detailed exposition of language features, and fails to convey the feel of the language.

The biggest disappointment to me in the entire four-volume set is the chapter on Modula-3. Contributed by Fashad Nayeri, the chapter is a lightly edited version of the Modula-3 language specification. Modula-3's designers are quite proud of the brevity of the language definition, citing this as evidence of the language's simplicity. It is indeed a delightful, simple language, but the terse spec makes difficult reading for programmers, and provides very little understanding of what it feels like to program in Modula-3. (In fairness, a colleague read the chapter and felt that he learned a lot about what the language is like. It may or may not be relevant that he has a fair amount of Modula-2 experience.) Modula-3 is less well known than it deserves to be, especially given its influence on more popular languages such as Java. It's unfortunate that most readers will find it difficult to learn about Modula-3 from this chapter.

The section on Java and the chapter covering Ada95 are good but not particularly noteworthy. The only real problem is the lack of historical information for Java. Java's designers have been very quick to acknowledge that Java is mostly a collection of good ideas from other languages, but there is no mention of the precedents here.

Volume II: Imperative Programming Languages

Volume II covers four languages: Fortran, C, Pascal, and Icon. Additionally, there is a short chapter on intermediate languages.

The Fortran 95 and Turbo Pascal chapters are average fare, and one of them contains the one unfortunate example of overenthusiastic advocacy in all four volumes: a section that begins "Fortran 95 should be the language of choice for modern applications development." Apart from that, though, the authors do fair jobs of describing their languages. Glenn Grotzinger, who wrote about Turbo Pascal, is frank about Pascal's weaknesses (although it would have been interesting to see Kernighan's famous and excellent paper, "Why Pascal is Not My Favorite Programming Language," included as counterpoint).

Two chapters deal with C. The first is Dennis Ritchie's paper on the development of C. I had read the paper before, but found it just as informative and entertaining the second time. Like Stroustrup, Ritchie is an excellent chronicler of his language's history, and his evaluation is even more honest and balanced. The second C chapter is a serviceable tutorial on C programming by Steve Summit, maintainer of the Internet C FAQ.

The final chapter of this volume, covering Icon, is one of the delights of the whole set. Written by Ralph Griswold, Icon's designer, it is very readable, and manages to be thorough while concentrating the most attention on Icon's unusual features. The result is a chapter that succeeds in conveying what it is like to program in Icon, a unique, powerful, mind-expanding programming language.

Volume III: Little Languages and Tools

In terms of practicality, this volume is probably the jewel. The title is perhaps a little misleading; "Little Languages and Scripting Languages" may have been more appropriate. Many programmers will find the volume useful because it provides a nice introduction to a problem solving strategy---domain-specific little languages---that is not well known in many communities. Others will find it useful as a source of information about the three most popular scripting languages.

A lot of languages are covered---too many to name. The major ones are EQN, troff, awk, sed, SQL, Tcl/Tk, Perl, and Python. Mixed in are shorter treatments of several more of the common Unix special-purpose languages and a bevy of little languages for music. While it's unfortunate that there's no explicit coverage of regular expressions (one of the archetypal little languages), they do surface in one form or another in at least seven of the twelve chapters in this book. (Come to think of it, that may be a more persuasive testimony to their utility than a chapter extolling their virtues.)

Chapter 1 is a reprint of Jon Bentley's wonderful Programming Pearls column of 1986. Titled "Little Languages," the chapter focuses on Pic, the picture-drawing language for troff. I recall that the first time I read this article, I thought that the choice of Pic was somewhat odd, but it soon became apparent that Pic is a perfect choice for illustrating the "little language" approach to problem solving. Itself a small language for a special purpose, Pic has been used as an intermediate language for several other languages that are even smaller or more specialized (such as grap, scatter, and chem). And in the other direction, the implementation of Pic makes use of three other little languages: lex, yacc, and make. Bentley does an extraordinary job. It's nice that this paper is reprinted here; the other book in which it has been printed, the second Programming Pearls volume, is apparently out of print.

Other chapters covering EQN and the troff typesetting system round out the discussion of a system for book preparation that seems to have been uniquely suited to the "little language" approach.

(Three of the first four chapters deal with typesetting and formatting software, and unlike the original versions, these chapters were not prepared with the software being described. The result, which amused me and no doubt frustrated the editor, is that the figures don't always convey what they should. Some of the figures are supposed to show the results of mistakes, but the people who set this book for printing occasionally decided to "fix" the mistakes. In one case, the "before" and "after" pictures are identical! Fortunately, it's usually clear from the text what should have been there. My advice is to smile and take this as evidence of the importance of the "little languages" approach that put so much control directly in the hands of authors.)

Paul Hudak contributed chapter 3, "Domain-Specific Languages." At first glance it seems to cover much the same ground as chapter 1, although there is slightly more emphasis on the process of choosing language primitives that match the domain. However, this chapter recommends a different approach to implementation. Whereas Bentley advocates building completely new little languages using specialized tools for language construction, Hudak recommends customizing an existing language with new procedures tailored for the problem domain. Functional languages work well for this purpose, and Hudak brings Haskell, the functional language he helped to design, into the discussion to demonstrate the approach.

Two of the chapters on scripting languages deserve mention, one for its failings and the other for its strengths. The "Perl Basics" chapter (one of two covering Perl) is unfortunately weak. Although I'm fond of Perl, it is definitely not an ordinary language, and it would be helpful for most readers to begin with some discussion of Perl's philosophy and design principles, as an orientation. (This is especially true since the designer of Perl consciously and deliberately ignored many of the traditional principles of language design---uniformity and orthogonality, for example---that are recommended by authors of earlier chapters.) Instead, the chapter launches directly into a discussion of datatypes, and moves on from there into other nuts-and-bolts topics. There are plentiful examples, but few of them are long enough to provide much of a feel for what Perl programs are really like. In their eagerness to cover all of the basics, they give short shrift to some of Perl's distinguishing characteristics, such as the context-sensitive behavior of many operations. Some of the most distinctive and oft-used Perl features, including the <FILEHANDLE> input operator, are used without any explanation at all. I suspect few readers will understand from this chapter why so many people get excited about Perl.

The scripting language of the moment, it seems, is Python. Mark Lutz, a well known Python advocate and the author of O'Reilly's Programming Python book, has done a terrific job of introducing Python. The coverage is broad enough that I felt as though I learned the whole language, although I know some details were omitted to keep the chapter short. The writing style is light and irreverent without being irritating. I had looked at Python on other occasions and had come away unimpressed, but Lutz almost made a believer out of me. I'm still in the camp of those who dislike Python's indentation-based syntax, but I'm coming around. (A cynic might make much of the fact that Lutz didn't mention the dependence on indentation until 30 pages into the chapter. Fortunately, I'm not a cynic.)

The final chapter of this volume returns to the topic of small, domain-specific languages. "Little Music Languages" was written by Peter S. Langston, a noted researcher on topics involving the combination of computers and music. The chapter introduces 16 little languages for describing and generating music. Most of them were designed for use by programs, rather than people (and a few even use binary encodings) but several can be read and understood easily by musicians, and most can be written or modified, if necessary, using a standard text editor. The chapter is fun to read and very informative. Furthermore, it is a significant contribution to the volume as a whole: to really understand the value of little languages it helps to see a broad sampling from vastly different domains. These languages are very different from the other languages in the book, and it is easy to see how the specialized languages are helpful to people working with computers and music. My only complaint is that there is no hint of how to acquire some of these tools to play with them, or whether that is even possible.

Volume IV: Functional and Logic Programming Languages

As a complete volume, this is the weakest of the four. It contains some excellent material, but ultimately it is a too-narrow treatment of an important topic, due to the few languages that are represented. In fact, at some level only two languages are discussed: Lisp and Prolog. Several dialects of Lisp are mentioned, but seen as functional languages they are mostly the same. Prolog as the single example of logic languages is reasonable, but the spectrum of functional languages is much larger than what is shown here.

Emacs Lisp is used to introduce basic Lisp programming. I initially thought Emacs Lisp was a bizarre choice, but it actually makes sense. Emacs Lisp is almost certainly the most common Lisp implementation in the world, and readers who want to try Lisp programming for the first time will find it relatively easy to gain access to a copy of Emacs. Additionally, among currently popular Lisps, Emacs Lisp is especially close to the early MacLisp dialects that had so much influence on the Lisp community. The chapter on Emacs Lisp is an excellent introduction to the Lisp language. The author steers clear of Emacs Lisp's specialized types and facilities for text editing. Those things are interesting in their own right, but are wisely omitted from an introduction to Lisp as a general-purpose language.

Other chapters deal with Scheme, Guile (an embeddable Scheme interpreter), and CLOS---all dialects of Lisp. The chapters all have their virtues. The Scheme chapter is the best short description of Scheme I've read. The CLOS chapter is a terrific introduction to a system that is unusually powerful and flexible by any measure. And the discussion of Guile centers on the use of embeddable interpreters and command languages in larger applications. This is a powerful application structuring mechanism, and although it is the niche that Tcl was designed for, the Tcl chapter in volume III never mentions it, so it is nice to see it discussed here.

For all their strengths, though, only the Scheme chapter tells us any more about functional programming than we learned in the Emacs Lisp chapter.

Prolog is the topic of the final chapter of the book. Of all the well known languages in these four volumes, Prolog is the only one I've never written even a single line of code in, and my only prior reading on the topic consisted of a couple of sketchy articles in Byte magazine years ago. So I was interested in reading about what I had always thought of as a very limited, impractical language. The author shows how Prolog can do more than just evaluate assertions based on chains of reasoning. At the same time, he does not try to claim that Prolog is a good choice for every task. He concludes with several complete examples, including an implementation of Conway's "Game of Life" and a Lisp interpreter. Although I'm obviously no authority, this chapter seems to be an excellent introduction to logic programming.

Concluding Thoughts

In addition to the scale, any project like this comes accompanied by a couple of big pitfalls. First, any large, multi-author book is bound to suffer from uneven style and quality of writing. Second, no matter which languages are covered, some people will complain that their favorite has been omitted. (It might be more hazardous to prepare a Handbook of Unix Text Editors, but not by much.)

For the most part, these books avoid the first problem. The various authors have different writing styles, of course, and cover their material at different levels of detail. With just a few exceptions, however, the quality of writing is high and the material is sound.

The second pitfall was not---in this reviewer's opinion---so well avoided. Although many interesting languages are represented here in addition to some old standbys, nearly every reader will find reason to wonder about some of the omissions. Despite my comments about the size of the project as it stands, my primary complaint is nevertheless that languages were omitted that should have been included. (There's precedent for this. Bjarne Stroustrup has noted that, during preparation of his book The Design and Evolution of C++, every review was of the form "This book is too long ... please add information on topics X, Y, and Z." So at least I'm in good company.)

I've mentioned the omission of languages more than once, and it may seem as though I'm belaboring a point. I do think these volumes are excellent as they stand, and belong on the shelves of every computer science library. However, when reading a review like this one, or scanning the table of contents in a bookstore, one tends to see what's present and not notice the omissions until later. So it seems reasonable to mention some languages that do not appear.

Volume I contains a good selection of object-oriented languages, but would have been completed by discussion of a language that is not class-based, such as Self. Not only are such languages quite different and interesting when compared to more conventional object-oriented languages, recent research seems to indicate that prototype-based OO languages are in some sense more fundamental: class-based OO languages can be modeled easily in terms of prototype-based languages, but doing it the other way around is more difficult.

Volume II contains some of the most surprising omissions: COBOL and PL/I are not included (although I confess that I personally didn't miss them). Additionally, I feel it would have been instructive to include Forth or some other stack-based language as an example of a very different approach, especially in conjunction with the chapter on intermediate languages.

Volume III is the most satisfying of the volumes in terms of completeness; it contains a broad cross-section of little languages and scripting languages. Still it's notable that the granddaddies of scripting languages (the Bourne shell and Rexx) are omitted. A discussion of macro processors such as m4 or the C preprocessor would have also been an interesting addition to the book.

Finally, Volume IV would have been greatly improved by broader coverage. APL warrants mention here (although I'm mindful that typesetting material about APL is expensive). There are actually several functional languages with some degree of current prominence; a chapter on Standard ML or Haskell would have provided some balance for the five Lisp chapters. And it would have been interesting to compare Prolog to Leda---a "multiparadigm" language that incorporates logic programming alongside imperative, functional, and object-oriented features.

To summarize: the Handbook of Programming Languages is an excellent compendium of information on a wide variety of languages. The overall quality is much higher than is typical for a multi-author work of this size. The only real problem is the omission of some important languages, but it is really a minor problem considering the huge scope of the books as they stand. The complete set will be of interest mainly to libraries and language buffs, but particular volumes will be very valuable to individuals.