cat posts/2026-08-10-sicp-reread.md

SICP, reread

· 6 min read · books programming lisp ai

The first time I tried to read SICP I got about halfway through chapter 3 and stopped. It was the mid-2000s, I was a C# programmer with a Turbo C childhood, and I’d heard enough people call it the best programming book ever written that I bought the purple paperback, installed a Scheme on Windows, and did the exercises in chapter 1 with real enthusiasm. Chapter 2 was harder and I did fewer of them. Then chapter 3 introduced assignment, and with it the environment model of evaluation, and somewhere in the boxes-and-arrows diagrams of frames pointing at frames I decided I understood it well enough and put it back on the shelf.

I picked it up again in June, the second edition from 1996, by Harold Abelson and Gerald Jay Sussman with Julie Sussman, which is free on MIT Press’s site. (There’s a JavaScript edition from 2022 as well, which I looked at and didn’t switch to. The parentheses are part of the point.) This time I finished it, partly out of stubbornness and partly because I wanted to know which parts would still feel essential to someone who barely writes code by hand anymore. It took most of the summer, mostly in the evenings, and I did far fewer exercises than I did at twenty-six, which I’ll come back to.

Chapter 1 is still the best chapter

It opens with “We are about to study the idea of a computational process,” and then spends the whole chapter on procedures and the processes they generate, with examples that are nearly all mathematics: square roots by Newton’s method, the Fermat test for primes, fixed points of functions, numerical derivatives. All that mathematics made it the comfortable chapter the first time, and it still was. What I didn’t appreciate in my twenties is how cleverly it’s built. By the end of the chapter you’ve seen Newton’s method three times, each time more abstract, until it’s a procedure that takes a function and returns a function, and you got there without anyone using the phrase “higher-order function” as if it were a big deal.

The distinction it makes between a procedure and the process it generates, a recursive procedure that produces an iterative process, is one I’ve used in interviews for years without remembering where I got it. It’s here, in section 1.2.

The part that matters more now

Chapter 2 is about data abstraction, and the idea it keeps returning to is the abstraction barrier. You build rational numbers out of a constructor and two selectors, and everything above that line uses only those three procedures, so you can change the representation underneath (reduce to lowest terms when you construct, or when you select) and nothing above the line notices. The book has a name for how you design that way: wishful thinking. You write the code that uses rational numbers as if the constructor and selectors already existed, and only then go and write them.

That’s very close to how I work with agents now, and I don’t think it’s a coincidence that it reads differently this time. When I write a spec, the thing I’m really doing is deciding where the barriers go. This module exposes these three functions, nothing outside it touches the table directly, the agent can do whatever it likes below the line. The spec work I wrote about last month is mostly that. When an agent makes a mess, it’s nearly always because it crossed a barrier I hadn’t stated, reaching into another module’s internals because that was the shortest path to a passing test. A person on the team would usually feel that was wrong, even without being told. The agent doesn’t feel anything about it, so the barrier has to be written down.

The preface has the line everyone quotes, that “programs must be written for people to read, and only incidentally for machines to execute.” I used to read that as a style tip. It reads more like a job description in 2026. Most of the code I deal with now was written by a machine, and I’m the person it has to be readable for.

Chapter 3, finally

The environment model is fine. I don’t know what I found so hard about it. Twenty years of debugging closures in JavaScript and lambdas in C# might have helped, since that’s what it is: a precise account of where a variable’s value comes from when a function was defined in one place and is called from another. I suspect I gave up the first time because I wanted to get to the streams section, and the book won’t let you skip.

What struck me this time was how much of the chapter is about the cost of assignment. The moment you allow set!, the simple substitution model from chapter 1 stops working, and you need the whole apparatus of frames and environments to say what a program means. Then concurrency makes it worse, and the book spends a section showing exactly how. Then streams are offered as a way of having state-like behavior without assignment. It’s a forty-year-old argument for functional programming that’s more careful than most of the ones I’ve read since.

Chapter 4 and the harness

Chapter 4 is the metacircular evaluator, a Scheme interpreter written in Scheme, and it opens with a sentence I underlined: “The evaluator, which determines the meaning of expressions in a programming language, is just another program.” Everything magical about the language turns out to be two procedures, eval and apply, calling each other in a loop.

I reviewed Crafting Interpreters a few years ago and said it was the book that finally made interpreters feel ordinary to me. SICP does it in one section, more abstractly and with less hand-holding, and I’m glad I read Nystrom first. But reading it this year, I couldn’t stop seeing the agent loop in it. An agent harness is also just another program. The model proposes an action, the harness decides what it means and runs it, the result goes back in, round and round, and all the safety is in the part that decides what an expression is allowed to mean. I went through that loop at the mechanical level in December. SICP is a reminder that “the thing that decides what this means is ordinary code you can read” is an idea with a long history, and a good one.

I skimmed chapter 5, the register machines and the compiler. I’m sure it’s good. I’d run out of summer.

The quote I keep arguing with

MIT stopped teaching 6.001 from this book in 2007 and moved to a course in Python built around programming little robots. Sussman’s explanation, as reported from a talk he gave in 2009, was that engineering had changed. In 1980 you could understand your system all the way down, and the course taught you to build big things out of small things you understood completely. By the 2000s, he said, programming was more like science: you grab a library nobody fully documented, and you poke at it to see what it does.

I think he was right about 2009, and it’s much more true now. A lot of what I do with agents is poking at code I didn’t write and only partly read, to see what it does. But I came out of this reread thinking the conclusion MIT drew was backwards, at least for people like me. The less of the code you write yourself, the more you need the habits SICP drills: knowing where the abstraction barriers are, being able to say precisely what a piece of code means, being able to read an evaluator and see that it’s just another program. Poking only works if you have a model of what you’re poking at.

The exercises are where I’m least sure of myself. I did maybe a fifth of them this time. The rest I read, thought about for a minute, and moved on, and a couple of times I asked a model to check my answer, which felt a little like cheating at solitaire. The ones I did by hand are the ones I remember, which is roughly what the book would predict.