Session 30
Back to the Future

The Last Opening Exercise

We have one last chance to practice for the final exam. Let's write a function that we can use this week:

Write a function named (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 non-recursive.

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.

on the left, a photo of young Harry Potter, holding a book and a wand, captioned 'Eugene on the first day'; on the right, a photo of Daniel Radcliffe in the street, wearing a bathrobe and fuzzy slippers, brandishing a gun in each hand, captioned 'Eugene on the last day'
Eugene uses the crazy Harry Potter meme to describe his state of mind

By the end of the semester, I feel like a crazed madman. But like you...

on the left, a photo of young Harry Potter, holding a book and a wand, captioned 'Eugene on the first day'; on the right, a photo of Daniel Radcliffe in the street, wearing a bathrobe and fuzzy slippers, brandishing a gun in each hand, captioned 'Eugene on the last day'
ah, the deep sleep of the innocent

... 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 Huey 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 Huey?

The album cover of Hall and Oates's 1984 album Big Bam Boom.
We've had no mentions of Hall and Oates
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 Huey, a language for manipulating colors.

The language consists of two primitive colors, color 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!

Huey is a limited language, but we can now use it to play with colors.

A common question about this assignment is: How do I make the do-exp constructor do what it's supposed to do? The solution to our opening exercise does the hard hard part of that task.

A related question is: How do I make the do->assignments accessor do what it's supposed to do? The code file for our opening exercise has a solution for that one as well.

Are we done? For the semester, yes, but Huey could be much more. The programmer in me really wants to take the next steps. What would happen if we:

Now that Huey supports local variables in an environment, adding functions to the language is especially tempting...

Walk through ideas for adding functions to the language.

Our REPL would fast become... a full-fledged interpreter! And Huey would fast become 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

a meme of Phil Dunphy from Modern Family misunderstanding texting slang
I'm a hip dad.
(Don't ask my daughters.)

Answer three quick questions for me:

You might answer the last one in different ways:

Please answer seriously and honestly. This will help me improve the course.

The Past is Prologue

a three-panel XKCD comic on Lisp and Perl
XKCD on the creation of the universe: God crushes the Lisper's dream

The Idea

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, Spacewar!

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 Implementation

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:

an image of Page 13 of the Lisp 1.5 user's manual, which contains the definition of the Lisp interpreter in Lisp
Page 13 of the Lisp 1.5 Programmer's Manual

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: apply a function to its arguments and evaluate an expression.
    Note that eval began its existence as a helper function for apply,
    and evalquote is an interface procedure that kicks off the computation with an empty environment!
  • The program assumes the existence of only a few forms:
    • the functions cons, car, cdr, atom, and eq?
    • the form lambda, for creating functions
    • the special forms quote and cond
    • the values 't and nil
    't means true, while nil means both false and the empty list. My Racket implementation uses #t and #f internally, but those tokens do not appear in the interpreter or in programs the interpreter recognizes.
  • It implements the same five primitive functions and the same three special forms.
  • It adds label as a bonus: a way to create recursive functions. (Think letrec.)
  • It implements four helper functions using the same small set of primitives:
    • evcon evaluates a cond 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 is zip.

Now do you understand why syntax procedures are so important?

It is remarkable how much can be built out of so little.

The Result

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, Clojure, Scheme, Common Lisp, 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...

a screenshot of the SuperCollider programming environment
SuperCollider in action

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 BF 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 on Day 1 of this course:

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 2026:

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 three quizzes: 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:

The cumulative parts of the exam will be much like Quizzes 1-3. Among the important topics on the exam are:

Wrap Up