This material is excerpted and adapted from Hal Abelson, “Introductory Undergraduate Subjects in Computer Science,” which appeared in Syllabus, February 1994.
MIT’s entry-level computing subject, 6.001, emphasizes controlling the complexity of software systems through general techniques common to all engineering design: building abstractions to hide details and to separate specification from implementation, establishing conventional interfaces to allow the creation of standard modules, and shifting modes of linguistic description. Although the course incorporates a great deal of programming, its intellectual focus is not on particular programming-language constructs, data structures, or algorithms — these are regarded as details. Instead, students are brought to appreciate a diversity of major programming paradigms: data abstraction, rule-based systems, object-oriented programming, functional programming, logic programming, and constructing embedded interpreters. Beyond that, there is a central concern with the technology of implementing languages and linguistic support for programming paradigms. Students are encouraged to regard themselves as language designers and implementors rather than only language users.
6.001 differs from typical introductory computer science subjects in using Scheme (a block-structured dialect of Lisp) rather than Pascal as its programming vehicle. The subject’s developers feel strongly that Pascal is hopelessly constraining, and that important ideas (such as functional programming and object-oriented programming) can be addressed within Pascal only awkwardly, if at all. In addition, they consider top-down hierarchical design, so often emphasized as a central theme in computer programming subjects, to be a minor and relatively simplistic strategy in the programmer’s arsenal for attacking complex problems.
The course begins by introducing simple programs, written in functional style (no assignment statements). It discusses substitution semantics, the evolution of processes, orders of growth, and the use of higher-order procedures. This leads to a treatment of compound data (sequences and trees) and symbol manipulation, including data abstraction and generic operations. Next comes a study of modularity in large-scale systems, introducing two different techniques for maintaining modularity: object-oriented programming with encapsulated local state; and functional programming with delayed evaluation. The course then turns to the study of interpreters. It presents a full interpreter for Scheme, and, for comparison, an interpreter for a logic programming language similar to pure Prolog. In the final section, which deals with register machines, the Scheme interpreter is implemented for a simple register machine, and the register machine implementation is augmented by a compiler that translates Scheme source code to register machine instructions.
There are two exams during the semester, and a final exam, but the crucial learning done by students is through substantial weekly programming assignments. These focus on reading and modifying significant systems, rather than writing small programs from scratch. Examples include an event-driven object-oriented simulation game, a conversational program that uses rules and pattern matching, symbolic algebra of polynomials and rational functions, interpreters for various languages, and a compiler with register optimization.
The course is required of all MIT undergraduates majoring in either Electrical Engineering or Computer Science, and is recommended for other majors where computation pays a major role. It is offered every semester and taken by over 500 students each year — half to two-thirds of all MIT undergraduates. Of the students enrolled in the subject (normally in their freshman and sophomore years) more than three-quarters have had previous programming experience, although hardly any at the level of sophistication of 6.001.
MIT students regard 6.001 as an extremely challenging, but highly successful subject, and they are uniformly impressed by the amount of material they have mastered by the end of the semester. The Department of Electrical Engineering and Computer Science provides considerable support for students enrolled in the subject. There is a large lecture that meets twice a week, and recitation sections of 20-30 students (typically taught by faculty) that meet twice a week. There are also regular weekly tutorials, where students meet in groups of three with a graduate TA to review homework and other course material. In addition, there is a large group of undergraduate assistants who staff the programming laboratory.
The 6.001 textbook Structure and Interpretation of Computer Programs, by H. Abelson, G.J. Sussman, and J. Sussman (MIT Press and McGraw-Hill) is used at more than 100 colleges and universities. The web site maintained by MIT Press at
http://mitpress.mit.edu/sicp/ contains material to assist instructors using the book. This includes excerpts from the book, a collection of sample assignments, and information on where to obtain implementations of Scheme.
Costanza asked Sussman why MIT had switched away from Scheme for their introductory programming course, 6.001. This was a gem. He said that the reason that happened was because engineering in 1980 was not what it was in the mid-90s or in 2000. In 1980, good programmers spent a lot of time thinking, and then produced spare code that they thought should work. Code ran close to the metal, even Scheme — it was understandable all the way down. Like a resistor, where you could read the bands and know the power rating and the tolerance and the resistance and V=IR and that’s all there was to know. 6.001 had been conceived to teach engineers how to take small parts that they understood entirely and use simple techniques to compose them into larger things that do what you want.
But programming now isn’t so much like that, said Sussman. Nowadays you muck around with incomprehensible or nonexistent man pages for software you don’t know who wrote. You have to do basic science on your libraries to see how they work, trying out different inputs and seeing how the code reacts. This is a fundamentally different job, and it needed a different course.
So the good thing about the new 6.001 was that it was robot-centered — you had to program a little robot to move around. And robots are not like resistors, behaving according to ideal functions. Wheels slip, the environment changes, etc — you have to build in robustness to the system, in a different way than the one SICP discusses.
And why Python, then? Well, said Sussman, it probably just had a library already implemented for the robotics interface, that was all.