What is the best way to talk about the general topic of programming languages? Certainly, the most common approach adopts the pattern of “compare features X, Y, Z in languages A, B, and C,” but this often leaves the reader overwhelmed with details, unable to see the fundamentals that lie beneath. A small number of texts break this mold. Tennent’s book [1], inspired by the school of semantics started by Christopher Strachey and Dana Scott, presents an analytical overview of language features, and provides the reader with critical tools such as the “principle of correspondence,” which allows the reader to view languages in a new way.
This book, first published in 1991, with new editions in 2001 and 2008, has been similarly influential. Friedman and Wand present the various features of languages by means of a series of interpreters written in the Scheme dialect of Lisp. The advantage of this approach is that everything is executable; therefore, readers are able to see directly the effect of their semantic choices on the meaning of sample programs.
The text begins by covering traditional ground: expression evaluation, parameter mechanisms, and state. Once a direct approach has been presented, this is contrasted with more efficient mechanisms, such as continuation passing (CPS) and trampolining interpreters. To support readers moving to these more sophisticated styles, Friedman and Wand provide “recipes” for making the conversion to CPS. The last third of the book adds a treatment of types, type checking, modules, and objects--much of which is new to this edition.
The foreword to the new edition makes a point about the variety of new frameworks for programming, particularly those involving the Web, that have become available since the book was first written, and the role that interpreters can play in supporting these new technologies. Central to many of these new technologies is concurrency, and while this is treated here through a discussion of threads, Friedman and Wand do not take the opportunity to discuss any alternative approaches, such as message passing between communicating processes. However, a comprehensive appendix gives an abundance of suggestions for further reading.
This is a text that combines an insightful analysis of the building blocks of programming languages with an excellent practical tutorial on how to write interpreters. Because of this, I highly recommend it to students, academics, and practitioners alike.