Session 30
Back to the Future
The Last Opening Exercise
One last chance to practice for the final exam, with a useful result:
(cons-at-end v lst)
that
returns a new list containing the items in lst
followed by v
.
list ::= () | (item . list)For example:
> (cons-at-end 'e '(a b 1 d)) '(a b 1 d e) > (cons-at-end 'a '()) '(a)
Your function can be recursive or use higher-order functions.
Humor me.
You can use (cons-at-end v lst)
in the constructor
for do
expressions on
Homework 11.
This code file
contains a couple of solutions, plus some help with the tricky
accessor for do
expressions.
Today
I usually start the semester brimming with energy and excitement. By the end of the semester, I feel like a crazed madman. But like you...
... I'm really just tired. A good kind of tired, after a semester thinking interesting thoughts and learning new things.
Today we take the idea of our Boom interpreter, and our study of programming languages, back to their historical beginning. In doing so, we will see one of the great intellectual achievements in computer science, one that has changed the world.
Along the way, I'll ask you to do one last exercise. No code, I promise.
What of Boom?

in class yet this semester.
I can't go for that.
If crazed Eugene, the one in the bathrobe and slippers, were behind the wheel today, we would tackle one more big task.
With the completion of Homework 11, you will have implemented an interpreter for Boom, a language for manipulating numbers.
The language consists of three primitive values, number literals, unary operations, binary operations, variable references, and blocks that support local variables and assignment statements.
The interpreter consists of a preprocessor, an evaluator, and a REPL, all three of which operate over the abstract syntax of the language.
That's two long lists. You can be proud!
Boom is a limited language, but we can now use it to play with numbers and solve simple number problems.
I have answered a couple of questions several times in the last few days, one from the beginning of the assignment and one from the middle:
- Cells... wat?
-
How do I make the
do-exp
constructor do what it's supposed to do? The solution to our opening exercise does the hard part of that task.
Are we done? For the semester, yes, but Boom could be much more. The programmer in me really wants to take the next steps. What would happen if we:
-
add boolean values and an
if
expression - add functions, even recursive ones (how?)
- add the ability to load Boom code from a file into the REPL
- add the ability to import Boom code into a program
- add the ability to display numbers in binary or hexadecimal
- add state to the REPL
- add more operators, both core and sugar
Now that Boom supports local variables in an environment, adding functions to the language is especially tempting...
Our REPL would fast become... a full-fledged interpreter! And Boom would fast becoming a real language.
You've implemented most of it, and you know how to implement the rest. We've come a long way in fifteen weeks.
A Quick Closing Survey

(Don't ask my daughters.)
Answer three quick questions for me:
- The three most important ideas I learned this semester were...
- The most surprising thing I learned this semester was...
- The most WTF? thing I learned this semester was...
You might answer the last one in different ways:
- I still don't understand XXX.
- I have no idea why XXX is part of the course.
Please answer seriously and honestly. This will help me improve the course.
The Past is Prologue

The duality of program and data means that anyone can create a language and write an interpreter for it.
This is not a new idea. It is one of the oldest ideas in computer science. People began to write language interpreters and compilers in the middle 1950s, in assembly language. Soon after that, John McCarthy realized something that gave the idea its full power: we can write a language interpreter in the language being interpreted.
Actually, McCarthy did more: he defined the features of a new language, Lisp, in terms of the language features themselves. This is the idea of the meta-circular interpreter, consisting of two functions:
- a function that evaluates an expression, and
- a function that applies a function to its arguments.
These functions evaluate a program in a mutually recursive fashion.
This, too, is one of the most beautiful ideas in computing, as well as the mechanism and inspiration for modern-day interpreters and compilers.
Though McCarthy created Lisp, he did not implement the first Lisp interpreter. McCarthy developed Lisp as a theoretical exercise: an attempt to create a programming alternative to the Turing Machine using Alonzo Church's lambda calculus.
Steve Russell, one of McCarthy's graduate students, suggested that he could implement the theory in an IBM 704 machine language program. McCarthy laughed and told him, "You're confusing theory with practice...". Russell did it any way.
Thanks to Russell and the IBM 704, we also have the functions
named car
and cdr
— as well as
what is considered to be
the first video game
ever created!
McCarthy and Russell soon discovered that Lisp was more powerful than the language that they had planned to build as part of their theoretical exercise, and the history of computing was forever changed.
The syntax and semantics of Lisp programs are so sparse and so uniform that the McCarthy's universal Lisp interpreter consisted of about one page of Lisp code. Here it is, on Page 13 of the the Lisp 1.5 Programmer's Manual, published in 1962. (The permanent online home of the manual is at softwarepreservation.org.)
Here is Page 13:

But this is a computer program. Why settle for a JPG image from a 60-year-old technical report?
We are programmers, the dreamers of dreams!
So I implemented this universal Lisp interpreter in Racket.
It is the file
universal-lisp-interp.rkt
in today's zip file.
Let's study it for a few minutes...
- There are two main functions: evaluate an expression and apply a function to its arguments.
-
The program assumes the existence of only a few forms:
-
the functions
cons
,car
,cdr
,atom
, andeq?
-
the form
lambda
, for creating functions -
the special forms
quote
andcond
-
the values
't
andnil
't
means true, whilenil
meant both false and the empty list. My Racket implementation uses#t
and#f
internally, but they do not appear in the interpreter or in programs the interpreter recognizes. -
the functions
-
It implements the same five primitive functions and the same
three special forms. It adds
label
, a way to create recursive functions. -
It implements four helper functions using the same small set of
primitives:
-
evcon
evaluates acond
expression. -
evlis
evaluates a list of arguments. -
assoc
works like the Racket primitive we have used this semester. -
pairlis
takes two lists and "zips" them up as a list of pairs. We sometimes write a function similar to this on Homework 4 or Homework 5. Its more modern name iszip
.
-
Now do you understand why syntax procedures are so important?
It is remarkable how much can be built out of so little.
Alan Kay, the creator of modern object-oriented programming, often compares McCarthy's universal Lisp interpreter to Maxwell's equations in physics: a small, simple set of equations that capture a huge amount of understanding and enable a new way of thinking.
I sometimes think of the components of this program as the basic particles out of which all computation is built, akin to the atomic theory of matter. Out of these few primitives, all programs can be built.
I know this probably excites me more than you. But we are still so close to our history. John McCarthy died in October 2011, during a previous offering of this course. Steve Russell is still alive. Unlike other sciences, computer science is still young, and many of its creators are still with us. What a gift.
As Paul Graham writes:
It's hard for us now to understand what a conceptual leap that was at the time. Paradoxically, one of the reasons his achievement is hard to appreciate is that it was so successful. Practically every programming language invented in the last 20 years [now 30+] includes ideas from Lisp, and each year the median language gets more Lisplike.
We see the original DNA of McCarthy's ideas and Russell's code in the tools we use today. This interpreter is at the base of Racket, Scheme, Common Lisp, Clojure, Dylan, and many other languages — but it is also fundamentally the core of every language you use. Don't miss the opportunity to appreciate big ideas, or where computer science comes from.
But this isn't just archeology; the same ideas drive language design and implementation today. That means they also drive the programming you do today. Consider this white paper that made the rounds a few years ago. A new syntactic abstraction in Java may be coming your way soon...

In the end, the duality of program and data, and the idea of language that bridges the gap between the two, make all programming possible. Even something as ambitious as SuperCollider, a programming environment for real time audio synthesis and algorithmic composition. It's just a language, with an interpreter that process programs written in it. Created by people just like you and me.
If you want to play with Forth or Joy, or experiment with different ways to pass parameters, or invent a new language that will change the world, you can do this, too.
This is, in a very real way, a rather long answer to a common question from students: Why Racket?
If you'd like to read more about the history and importance of McCarthy's Lisp, check out Paul Graham's essay, The Roots of Lisp.
John McCarthy did much more than create Lisp.
I first learned about McCarthy not from Lisp but from my first love, AI. McCarthy coined the term "Artificial Intelligence" when organizing (along with Minsky, Rochester, and Shannon) the 1956 Dartmouth workshop that gave birth to the field.
I studied McCarthy's work in AI using the language he had created. To me, he was a giant of AI long before I recognized that he was giant of programming languages, too.
Like many pioneers of our field, he laid the groundwork in many subdisciplines. They had no choice; they had to build their work out of ideas using only the rawest materials.
McCarthy is even credited with the first public descriptions of time-sharing systems and what we now call cloud computing. For McCarthy's 1970-era predictions about home computers and the cloud, see his paper The Home Information Terminal, reprinted in 2000.
The Big Ideas
My thoughts about the most important ideas we study in this course change from semester to semester. Here is a list of five for 2023:
- There is life without side effects.
- Higher-order procedures for the win.
- ... but when I do, I use structural recursion.
- Your favorite essential language feature may be an abstraction in the language interpreter.
- #1. Program == Data. This is the duality that creates computer science.
The Future: The Final Exam
The final is cumulative, with an emphasis on material since Quiz 3: on data abstractions, your interpreter, and our "Bringing It All Together" sessions. It will look like our first three exams: a mix of short-answer questions and programming exercises, with perhaps a few even-shorter-answer questions.
Since Quiz 3, we have studied a few ideas:
- finite function, as a sandbox for different ways to implement a data type (We used a finite function as an environment in our interpreter.)
- state, shared variables, and objects
- BF (optimization)
- Joy (new paradigm)
- your Huey interpreter
The cumulative parts of the exam will be much like Quizzes 1-3. Among the important topics on the exam are:
- basic Racket
- functional programming and higher-order functions
- structural recursion patterns, including interface procedures and mutual recursion
- syntactic abstraction
- variables: binding, scope, and lexical addressing
Wrap Up
-
Reading
- Nothing new, of course. Study the notes and code from the previous two sessions, which talk a bit about the past and the future. Study the code of your Huey interpreter.
-
Homework
- Homework 11 is available and due tomorrow.
-
Final Exam
-
The final exam is scheduled for Thursday, May 15, in the
1:00 PM-2:50 PM time slot.
See you then. Drop me email or drop by the office hours if you have any questions or comments.
-
The final exam is scheduled for Thursday, May 15, in the
1:00 PM-2:50 PM time slot.