A course in computer programming} provides the typical student’s first exposure to the field of computer science. Most students in such a course will have used computers all their lives, for social networking, email, games, web browsing, word processing, and a host of other tasks, but it is not until they write their first programs that they begin to appreciate how applications work. After gaining a certain level of facility as programmers (presumably with the help of a good course in data structures and algorithms), the natural next step is to wonder how programming languages work. This book provides an explanation. It aims, quite simply, to be the most comprehensive and accurate languages text available, in a style that is engaging and accessible to the typical undergraduate. This aim reflects our conviction that students will understand more, and enjoy the material more, if we explain what is really going on.
In the conventional “systems” curriculum, the material beyond data structures (and possibly computer organization) tends to be compartmentalized into a host of separate subjects, including programming languages, compiler construction, computer architecture, operating systems, networks, parallel and distributed computing, database management systems, and possibly software engineering, object-oriented design, graphics, or user interface systems. One problem with this compartmentalization is that the list of subjects keeps growing but the number of semesters in a Bachelor’s program does not. More important, perhaps, many of the most interesting discoveries in computer science occur at the boundaries between subjects. Computer architecture and compiler construction, for example, have inspired each other for 70 years, through generations of supercomputers, pipelined microprocessors, multicore chips, and modern GPUs. Over the past two decades, advances in virtualization and container technology have served to blur the distinctions among hardware, operating systems, compilers, and language run-time systems, driving the explosive growth of cloud computing. Programming language technology is now routinely embedded in everything from dynamic web content, to gaming and entertainment, to security and finance.
Increasingly, both educators and practitioners have come to emphasize these sorts of interactions. Within higher education in particular, there is a growing trend toward integration in the core curriculum. Rather than give the typical student an in-depth look at two or three narrow subjects, leaving holes in all the others, many schools have revised the programming languages and computer organization courses to cover a wider range of topics, with follow-on electives in various specializations. This trend is very much in keeping with the ACM/IEEE-CS Computer Science Curricula 2013 guidelines, which emphasize the need to manage the size of the curriculum and to cultivate both a “system-level perspective” and an appreciation of the interplay between theory and practice. In particular, the authors write
Graduates of a computer science program need to think at multiple levels of detail and abstraction. This understanding should transcend the implementation details of the various components to encompass an appreciation for the structure of computer systems and the processes involved in their construction and analysis [p. 24].
On the specific subject of this text, they write
Programming languages are the medium through which programmers precisely describe concepts, formulate algorithms, and reason about solutions. In the course of a career, a computer scientist will work with many different languages, separately or together. Software developers must understand the programming models underlying different languages and make informed design choices in languages supporting multiple complementary approaches. Computer scientists will often need to learn new languages and programming constructs, and must understand the principles underlying how programming language features are defined, composed, and implemented. The effective use of programming languages, and appreciation of their limitations, also requires a basic knowledge of programming language translation and static program analysis, as well as run-time components such as memory management [p. 155].
The first four editions of Programming Language Pragmatics (PLP) had the good fortune of riding the trend toward integrated understanding. This fifth edition continues and strengthens that tradition, with new material on both theoretical and practical aspects of programming language design and implementation.
At its core, PLP is a book about how programming languages work. Rather than enumerate the details of many different languages, it focuses on concepts that underlie all the languages the student is likely to encounter, illustrating those concepts with a variety of concrete examples, and exploring the tradeoffs that explain why different languages were designed in different ways. Similarly, rather than explain how to build a compiler or interpreter (a task few programmers will undertake in its entirety), PLP focuses on what a compiler does to an input program, and why. Language design and implementation are thus explored together, with an emphasis on the ways in which they interact.
From our own perspective, the biggest change in the fifth edition is of course the addition of Jonathan Aldrich as co-author. Prof. Aldrich brings extensive expertise in type systems, formal semantics, and software engineering, together with a fresh eye for material throughout the text. This has allowed us to undertake a major re-write of Chapters 4 (Program Semantics) and 7 (Type Systems), and to make substantial changes to Chapters 8 (Composite Types), 10 (Object Orientation), and 15 (Building a Runnable Program) as well. Among other things, we have replaced semantic analysis and code generation techniques based on attribute grammars with techniques based on big-step formal inference rules. (The attribute grammar material formerly in Chapter 4 can now be found on the companion site.) We have also added coverage of formal subtyping (Sections 7.1.2 and 7.2.2), refinement and liquid types (Section 7.1.5), Hindley–Milner type inference (Section 7.4.1), template “concepts” in C++20 (Section C-7.3.5), and ownership types (Section 8.5.5) and safe concurrency (Sections 13.4.1 and 13.5.1) in Rust.
In other chapters, we have introduced significant amounts of new or revised material on iterators (Section 6.5.3), move constructors and assignment (Section 9.3.1), asynchronous programming (Section 9.7), mix-ins and traits (Section 10.5), TypeScript (Section 14.4.4), WebAssembly (Section 15.2.3), and the intermediate representations of the LLVM compiler infrastructure (Section C-15.2.1). To make room for these additions, we have moved coverage of web scripting (Section C-14.3) to the companion site, and have condensed material on a variety of less important topics. Hundreds or thousands of other small changes have been made throughout. We have addressed all known errata (though we probably added some more); increased the number of examples in newer languages (Swift, Go, Rust, Scala, JavaScript, TypeScript); eliminated some of the remaining examples in older languages (Pascal, Modula, Ada, Scheme, Perl); modified several code fragments to be more idiomatic; updated material in Chapters 5, 15, and elsewhere to reflect advances in computer architecture; and added several languages to Appendix A and its genealogical chart.
Overall, the printed text has grown by roughly 30 pages. There are 7 more “Design & Implementation” sidebars, 54 more numbered examples, and about 60 new end-of-chapter exercises and explorations. Considerable effort has been invested in maintaining a consistent and comprehensive index. As in earlier editions, Morgan Kaufmann has maintained its commitment to providing definitive texts at reasonable cost: PLP-5e is far less expensive than competing alternatives, but larger and more comprehensive.
To minimize the physical size of the text, make way for new material, and allow students to focus on the fundamentals when browsing, over 400 pages of more advanced or peripheral material can be found on a companion web site. Each companion-site (CS) section is represented in the main text by a brief introduction to the subject and an “In More Depth” paragraph that summarizes the elided material.
Note that placement of material on the companion site does not constitute a judgment about its technical importance. It simply reflects the fact that there is more material worth covering than will fit in a single volume or a single-semester course. Since preferences and syllabi vary, most instructors will probably want to assign reading from the CS, and most will refrain from assigning certain sections of the printed text. Our intent has been to retain in print the material that is likely to be covered in the largest number of courses.
Also included on the CS are runnable copies of all significant code fragments found in the text (in more than two dozen languages).
Like its predecessors, PLP-5e places heavy emphasis on the ways in which language design constrains implementation options, and the ways in which anticipated implementations have influenced language design. Many of these connections and interactions are highlighted in some 145 “Design & Implementation” sidebars. A more detailed introduction appears in Sidebar 1.1. A numbered list appears in Appendix B.
Examples in PLP-5e are intimately woven into the flow of the presentation. To make it easier to find specific examples, to remember their content, and to refer to them in other contexts, a number and a title for each is displayed in a marginal note. There are well over 1000 such examples across the main text and the CS. A detailed list appears in Appendix C.
Review questions appear throughout the text at roughly 10-page intervals, at the ends of major sections. These are based directly on the preceding material, and have short, straightforward answers.
More detailed questions appear at the end of each chapter. These are divided into Exercises and Explorations. The former are generally more challenging than the per-section review questions, and should be suitable for homework or brief projects. The latter are more open ended, requiring web or library research, substantial time commitment, or the development of subjective opinion. Solutions to many of the exercises (but not the explorations) are available to registered instructors from a password-protected web site: visit www.elsevier.com/books-and-journals/book-companion/9780323999663.
Programming Language Pragmatics covers almost all of the material in the PL “knowledge units” of the Computer Science Curricula 2013 report. The languages course at the University of Rochester, for which this book was originally designed, is in fact one of the featured “course exemplars” in the report (pp. 369–371). Figure 1 illustrates several possible paths through the text.
Figure 1 Paths through the text. Darker shaded regions indicate supplemental “In More Depth” sections on the companion site. Section numbers are shown for breaks that do not correspond to supplemental material.
For self-study, or for a full-year course (track F in Figure 1), we recommend working through the book from start to finish, turning to the companion site as each “In More Depth” section is encountered. The one-semester course at Rochester (track R) also covers most of the book, but leaves out most of the CS sections, as well as bottom-up parsing (Section 2.3.4), logic languages (Chapter 12), and the second halves of Chapters 15 (Building a Runnable Program) and 16 (Run-time Program Management). Note that the material on functional programming (Chapter 11 in particular) can be taught in either OCaml or Scheme.
Some chapters (2, 4, 5, 15, 16, 17) have a heavier emphasis than others on implementation issues. These can be reordered to a certain extent with respect to the more design-oriented chapters. Many students will already be familiar with much of the material in Chapter 5, most likely from a course on computer organization; hence the placement of the chapter on the companion site. Some students may also be familiar with some of the material in Chapter 2, perhaps from a course on automata theory. Much of this chapter can then be read quickly as well, pausing perhaps to dwell on such practical issues as recovery from syntax errors, or the ways in which a scanner differs from a formal finite automaton.
A traditional programming languages course (track P in Figure 1) might leave out all of scanning and parsing. It would also de-emphasize the more implementation-oriented material throughout. In place of these, it could add such design-oriented CS sections as multiple inheritance (Section 10.6), Smalltalk (Section 10.7.1), lambda calculus (Section 11.7), and predicate calculus (Section 12.3).
PLP has also been used at some schools for an introductory compiler course (track C in Figure 1). The typical syllabus leaves out much of Part III (Chapters 11 through 14), and de-emphasizes the more design-oriented material throughout. In place of these, it includes all of scanning and parsing, Chapters 15 through 17, and a slightly different mix of other CS sections.
For a school on the quarter system, an appealing option is to offer an introductory one-quarter course and two optional follow-on courses (track Q in Figure 1). The introductory quarter might cover the main (non-CS) sections of Chapters 1, 3, 6, 7, and 8, plus the first halves of Chapters 2 and 9. A language-oriented follow-on quarter might cover Chapter 4, the rest of Chapter 9, all of Part III, CS sections from Chapters 6 through 9, and possibly supplemental material on formal semantics, type theory, or other related topics. A compiler-oriented follow-on quarter might cover the rest of Chapter 2, Chapters 5 and 15–17, CS sections from Chapters 3, 9, and 10, and possibly supplemental material on automatic code generation, aggressive code improvement, programming tools, and so on.
Whatever the path through the text, we assume that the typical reader has
already acquired significant experience with at least one imperative
language. Exactly which language it is shouldn’t matter. Examples are
drawn from a wide variety of languages, but always with enough comments
and other discussion that readers without prior experience should be
able to understand easily.
Single-paragraph introductions to some 70 different languages
appear in Appendix A.
Algorithms, when needed, are presented in an
informal pseudocode that should be self-explanatory. Real
programming language code is set in "typewriter" font
.
Pseudocode is set in a sans-serif font.
In addition to supplemental sections, the companion site contains complete source code for all nontrivial examples, and a list of all known errors in the book. Additional resources are available on-line at www.elsevier.com/books-and-journals/book-companion/9780323999663. For instructors who have adopted the text, a password-protected page provides access to
In preparing the fifth edition, we have been blessed with the generous assistance of a very large number of people. Many provided errata or other feedback on the fourth edition, among them Carl Albing, Wentao Cai, Nicholas Coleman, Chen Ding, Samuel Estep, Alexander Gutierrez, Lucian Ilie, Andrew Jarvis, Tim Lee, Shuo (Max) Li, Sreepathi Pai, Colin Pronovost, Amila Senadheera, Yannis Smaragdakis, Ben Steele, Yanling Wang, Jingguo Yao, Zhizhou Zhang, and Zongren Zhang. We also remain indebted to the many individuals acknowledged in previous editions, and to the reviewers, adopters, and readers who made those editions a success.
In preparing the fifth edition, we have drawn on over 50 years of combined experience teaching programming language courses to upper-level undergraduates at the University of Rochester and at Carnegie Mellon University. We are grateful to all our students for their enthusiasm and feedback. Our thanks extend also to our colleagues and graduate students, and to the administrative, secretarial, and technical staffs of our respective departments for providing such a supportive and productive work environment.
Last and perhaps most important of all, we are indebted to our spouses and children for their patience and support through endless months of writing and revising. Computing is a fine profession, but family is what really matters.
Michael L. Scott
Jonathan Aldrich
August 2023