The Roos of Lisp paul graham Draf, January 18, 2002. In 1960, John McCarhy published a remarkable paper in which he did for programming somehing like wha Euclid did for geomery. 1 He showed how, given a handful of simple operaors and a noaion for funcions, you can build a whole programming language. He called his language Lisp, for \Lis Processing," because one of his key ideas was o use a simple daa srucure called a lis for boh code and daa. I's worh undersanding wha McCarhy discovered, no jus as a landmark in he hisory of compuers, bu as a model for wha programming is ending o become in our own ime. I seems o me ha here have been wo really clean, consisen models of programming so far: he C model and he Lisp model. These wo seempoins of high ground, wih swampylowlands beween hem. As compuers have grown more powerful, he new languages being developed have been moving seadily oward he Lisp model. A popular recipe for new programming languages in he pas 20 years has been o ake he C model of compuing and add o i, piecemeal, pars aken from he Lisp model, like runime yping and garbage collecion. In his aricle I'm going o ry o explain in he simples possible erms wha McCarhy discovered. The poin is no jus o learn abou an ineresing heoreical resul someone gured ou fory years ago, bu o show where languages are heading. The unusual hing abou Lisp in fac, he dening qualiy of Lisp is ha i can be wrien in iself. To undersand wha Mc- Carhy meanby his, we're going o rerace his seps, wih his mahemaical noaion ranslaed ino running Common Lisp code. 1 Seven Primiive Operaors To sar wih, we dene an expression. An expression is eiher an aom, which is a sequence of leers (e.g. foo), or a lis of zero or more expressions, separaed by whiespace and enclosed by parenheses. Here are some expressions: foo (foo) (foo bar) (a b (c) d) The las expression is a lis of four elemens, he hird of which is iself a lis of one elemen. 1 \Recursive Funcions of Symbolic Expressions and Their Compuaion by Machine, Par I." Communicaions of he ACM 3:4, April 1960, pp. 184{195. 1
In arihmeic he expression 1 + 1 has he value 2. Valid Lisp expressions also have values. If an expression e yields a value v we sayhae reurns v. Our nex sep is o dene wha kinds of expressions here can be, and wha value each kind reurns. If an expression is a lis, we call he rs elemen he operaor and he remaining elemens he argumens. We are going o dene seven primiive (in he sense of axioms) operaors: quoe, aom, eq, car, cdr, cons, and cond. 1. (quoe x) reurns x. For readabiliy we will abbreviae (quoe x) as 'x. > (quoe a) a > 'a a > (quoe ) 2. (aom x) reurns he aom if he value of x is an aom or he empy lis. Oherwise i reurns. In Lisp we convenionally use he aom o represen ruh, and he empy lis o represen falsiy. > (aom 'a) > (aom ') > (aom ') Now ha we have an operaor whose argumen is evaluaed we can show wha quoe is for. By quoing a lis we proec i from evaluaion. An unquoed lis given as an argumen o an operaor like aom is reaed as code: > (aom (aom 'a)) whereas a quoed lis is reaed as mere lis, in his case a lis of wo elemens: > (aom '(aom 'a)) This corresponds o he way we use quoes in English. Cambridge is a own in Massachuses ha conains abou 90,000 people. \Cambridge" is a word ha conains nine leers. 2
Quoe may seem a bi of a foreign concep, because few oher languages have anyhing like i. I's closely ied o one of he mos disincive feaures of Lisp: code and daa are made ou of he same daa srucures, and he quoe operaor is he way we disinguish beween hem. 3. (eq x y) reurns if he values of x and y are he same aom or boh he empy lis, and oherwise. > (eq 'a 'a) > (eq 'a 'b) > (eq ' ') 4. (car x) expecs he value of x o be a lis, and reurns is rs elemen. > (car ') a 5. (cdr x) expecs he value of x o be a lis, and reurns everyhing afer he rs elemen. > (cdr ') (b c) 6. (cons x y) expecs he value of y o be a lis, and reurns a lis conaining he value of x followed by he elemens of he value of y. > (cons 'a '(b c)) > (cons 'a (cons 'b (cons 'c '))) > (car (cons 'a '(b c))) a > (cdr (cons 'a '(b c))) (b c) 7. (cond (p 1 e 1 ) ::: (p n e n )) is evaluaed as follows. The p expressions are evaluaed in order unil one reurns. When one is found, he value of he corresponding e expression is reurned as he value of he whole cond expression. > (cond ((eq 'a 'b) 'firs) ((aom 'a) 'second)) second 3
In ve of our seven primiive operaors, he argumens are always evaluaed when an expression beginning wih ha operaor is evaluaed. 2 We will call an operaor of ha ype a funcion. 2 Denoing Funcions Nex we dene a noaion for describing funcions. A funcion is expressed as (lambda (p 1 :::p n ) e), where p 1 :::p n are aoms (called parameers) and e is an expression. An expression whose rs elemen is such an expression ((lambda (p 1 :::p n ) e) a 1 :::a n ) is called a funcion call and is value is compued as follows. Each expression a i is evaluaed. Then e is evaluaed. During he evaluaion of e, he value of any occurrence of one of he p i is he value of he corresponding a i in he mos recen funcion call. > ((lambda (x) (cons x '(b))) 'a) (a b) > ((lambda (x y) (cons x (cdr y))) 'z ') (z b c) If an expression has as is rs elemen an aom f ha is no one of he primiive operaors (f a 1 :::a n ) and he value of f isafuncion(lambda (p 1 :::p n ) e) hen he value of he expression is he value of ((lambda (p 1 :::p n ) e) a 1 :::a n ) In oher words, parameers can be used as operaors in expressions as well as argumens: > ((lambda (f) (f '(b c))) '(lambda (x) (cons 'a x))) There is anoher noaion for funcions ha enables he funcion o refer o iself, hereby giving us a convenien way odene recursive funcions. 3 The 2 Expressions beginning wih he oher wo operaors, quoe and cond, are evaluaed differenly. When a quoe expression is evaluaed, is argumen is no evaluaed, bu is simply reurned as he value of he whole quoe expression. And in a valid cond expression, only an L-shaped pah of subexpressions will be evaluaed. 3 Logically we don' need o dene a new noaion for his. We could dene recursive funcions in our exising noaion using a funcion on funcions called he Y combinaor. I may be ha McCarhy did no know abou he Y combinaor when he wroe his paper in any case, label noaion is more readable. 4
noaion (label f (lambda (p 1 :::p n ) e)) denoes a funcion ha behaves like (lambda (p 1 :::p n ) e), wih he addiional propery ha an occurrence of f wihin e will evaluae o he label expression, as if f were a parameer of he funcion. Suppose we wan o dene a funcion (subs x y z), which akes an expression x, an aom y, and a lis z, and reurns a lis like z bu wih each insance of y (a any deph of nesing) in z replaced by x. > (subs 'm 'b '(a b d)) (a m (a m c) d) We can denoe his funcion as (label subs (lambda (x y z) (cond ((aom z) (cond ((eq z y) x) (' z))) (' (cons (subs x y (car z)) (subs x y (cdr z))))))) We will abbreviae f = (label f (lambda (p 1 :::p n ) e)) as (defun f (p 1 :::p n ) e) so (defun subs (x y z) (cond ((aom z) (cond ((eq z y) x) (' z))) (' (cons (subs x y (car z)) (subs x y (cdr z))))))) Incidenally, we see here how o ge a defaul clause in a cond expression. A clause whose rs elemen is' will always succeed. So (cond (x y) (' z)) is equivalen o wha we migh wrie in a language wih synax as if x hen y else z 3 Some Funcions Now ha we have away of expressing funcions, we dene some new ones in erms of our seven primiive operaors. Firs i will be convenien o inroduce 5
some abbreviaions for common paerns. We will use cxr, where x is a sequence of as or ds, as an abbreviaion for he corresponding composiion of car and cdr. So for example (cadr e) is an abbreviaion for (car (cdr e)), which reurns he second elemen of e. > (cadr '((a b) (c d) e)) (c d) > (caddr '((a b) (c d) e)) e > (cdar '((a b) (c d) e)) (b) Also, we will use (lis e 1 :::e n ) for (cons e 1 ::: (cons e n ') ::: ). > (cons 'a (cons 'b (cons 'c '))) > (lis 'a 'b 'c) Now we dene some new funcions. I've changed he names of hese funcions by adding periods a he end. This disinguishes primiive funcions from hose dened in erms of hem, and also avoids clashes wih exising Common Lisp funcions. 1. (null. x) ess wheher is argumen is he empy lis. (defun null. (x) (eq x ')) > (null. 'a) > (null. ') 2. (and. x y) reurns if boh is argumens do and oherwise. (defun and. (x y) (cond (x (cond (y ') (' '))) (' '))) > (and. (aom 'a) (eq 'a 'a)) > (and. (aom 'a) (eq 'a 'b)) 3. (no. x) reurns if is argumen reurns, and if is argumen reurns. 6
(defun no. (x) (cond (x ') (' '))) > (no (eq 'a 'a)) > (no (eq 'a 'b)) 4. (append. x y) akes wo liss and reurns heir concaenaion. (defun append. (x y) (cond ((null. x) y) (' (cons (car x) (append. (cdr x) y))))) > (append. '(a b) '(c d)) (a b c d) > (append. ' '(c d)) (c d) 5. (pair. x y) akes wo liss of he same lengh and reurns a lis of woelemen liss conaining successive pairs of an elemen from each. (defun pair. (x y) (cond ((and. (null. x) (null. y)) ') ((and. (no. (aom x)) (no. (aom y))) (cons (lis (car x) (car y)) (pair. (cdr x) (cdr y)))))) > (pair. '(x y z) ') ((x a) (y b) (z c)) 6. (assoc. x y) akes an aom x and a lis y of he form creaed by pair., and reurns he second elemen of he rs lis in y whose rs elemen is x. (defun assoc. (x y) (cond ((eq (caar y) x) (cadar y)) (' (assoc. x (cdr y))))) > (assoc. 'x '((x a) (y b))) a > (assoc. 'x '((x new) (x a) (y b))) new 7
4 The Surprise So we can dene funcions ha concaenae liss, subsiue one expression for anoher, ec. An elegan noaion, perhaps, bu so wha? Now comes he surprise. We can also, i urns ou, wrie a funcion ha acs as an inerpreer for our language: a funcion ha akes as an argumen any Lisp expression, and reurns is value. Here i is: (defun eval. (e a) (cond ((aom e) (assoc. e a)) ((aom (car e)) (cond ((eq (car e) 'quoe) (cadr e)) ((eq (car e) 'aom) (aom (eval. (cadr e) a))) ((eq (car e) 'eq) (eq (eval. (cadr e) a) (eval. (caddr e) a))) ((eq (car e) 'car) (car (eval. (cadr e) a))) ((eq (car e) 'cdr) (cdr (eval. (cadr e) a))) ((eq (car e) 'cons) (cons (eval. (cadr e) a) (eval. (caddr e) a))) ((eq (car e) 'cond) (evcon. (cdr e) a)) (' (eval. (cons (assoc. (car e) a) (cdr e)) a)))) ((eq (caar e) 'label) (eval. (cons (caddar e) (cdr e)) (cons (lis (cadar e) (car e)) a))) ((eq (caar e) 'lambda) (eval. (caddar e) (append. (pair. (cadar e) (evlis. (cdr e) a)) a))))) (defun evcon. (c a) (cond ((eval. (caar c) a) (eval. (cadar c) a)) (' (evcon. (cdr c) a)))) (defun evlis. (m a) (cond ((null. m) ') (' (cons (eval. (car m) a) (evlis. (cdr m) a))))) The deniion of eval. is longer han any of he ohers we've seen before. Le's consider how each par works. The funcion akes wo argumens: e, he expression o be evaluaed, and a, a lis represening he values ha aoms have been given by appearing as 8
parameers in funcion calls. This lis is called he environmen, and i is of he form creaed by pair.. I was in order o build and search hese liss ha we wroe pair. and assoc.. The spine of eval. is a cond expression wih four clauses. How we evaluae an expression depends on wha kind i is. The rs clause handles aoms. If e is an aom, we look up is value in he environmen: > (eval. 'x '((x a) (y b))) a The second clause of eval. is anoher cond for handling expressions of he form (a :::), where a is an aom. These include all he uses of he primiive operaors, and here is a clause for each one. > (eval. '(eq 'a 'a) ') > (eval. '(cons x '(b c)) '((x a) (y b))) All of hese (excep quoe) call eval. o nd he value of he argumens. The las wo clauses are more complicaed. To evaluae a cond expression we call a subsidiary funcion called evcon., which works is way hrough he clauses recursively, looking for one in which he rs elemen reurns. When i nds such a clause i reurns he value of he second elemen. > (eval. '(cond ((aom x) 'aom) (' 'lis)) '((x '(a b)))) lis The nal par of he second clause of eval. handles calls o funcions ha have been passed as parameers. I works by replacing he aom wih is value (which ough o be a lambda or label expression) and evaluaing he resuling expression. So (eval. '(f '(b c)) '((f (lambda (x) (cons 'a x))))) urns ino (eval. '((lambda (x) (cons 'a x)) '(b c)) '((f (lambda (x) (cons 'a x))))) which reurns. The las wo clauses in eval. handle funcion calls in which he rs elemen is an acual lambda or label expression. A label expression is evaluaed by pushing a lis of he funcion name and he funcion iself ono he environmen, and hen calling eval. on an expression wih he inner lambda expression subsiued for he label expression. Tha is, 9
(eval. '((label firsaom (lambda (x) (cond ((aom x) x) (' (firsaom (car x)))))) y) '((y ((a b) (c d))))) becomes (eval. '((lambda (x) (cond ((aom x) x) (' (firsaom (car x))))) y) '((firsaom (label firsaom (lambda (x) (cond ((aom x) x) (' (firsaom (car x))))))) (y ((a b) (c d))))) which evenually reurns a. Finally, an expression of he form ((lambda (p 1 :::p n ) e) a 1 :::a n ) is evaluaed by rs calling evlis. o ge a lis of values (v 1 ::: v n ) of he argumens a 1 :::a n, and hen evaluaing e wih (p 1 v 1 ) :::(p n v n ) appended o he fron of he environmen. So (eval. '((lambda (x y) (cons x (cdr y))) 'a '(b c d)) ') becomes (eval. '(cons x (cdr y)) '((x a) (y (b c d)))) which evenually reurns (a c d). 5 Afermah Now ha we undersand how eval works, le's sep back and consider wha i means. Wha we have here is a remarkably elegan model of compuaion. Using jus quoe, aom, eq, car, cdr, cons, and cond, we can dene a funcion, eval., ha acually implemens our language, and hen using ha we can dene any addiional funcion we wan. There were already models of compuaion, of course mos noably he Turing Machine. Bu Turing Machine programs are no very edifying o read. If you wan a language for describing algorihms, you migh wan somehing more absrac, and ha was one of McCarhy's aims in dening Lisp. 10
The language he dened in 1960 was missing a lo. I has no side-eecs, no sequenial execuion (which is useful only wih side eecs anyway), no pracical numbers, 4 and dynamic scope. Bu hese limiaions can be remedied wih surprisingly lile addiional code. Seele and Sussman show how o do i in a famous paper called "The Ar of he Inerpreer." 5 If you undersand McCarhy's eval, you undersand more han jus a sage in he hisory of languages. These ideas are sill he semanic core of Lisp oday. So sudying McCarhy's original paper shows us, in a sense, wha Lisp really is. I's no somehing ha McCarhy designed so much as somehing he discovered. I's no inrinsically a language for AI or for rapid prooyping, or any oher ask a ha level. I's wha you ge (or one hing you ge) when you ry o axiomaize compuaion. Over ime, he median language, meaning he language used by he median programmer, has grown consisenly closer o Lisp. So by undersanding eval you're undersanding wha will probably be he main model of compuaion well ino he fuure. 4 I is possible o do arihmeic in McCarhy's 1960 Lisp by using e.g. a lis of n aoms o represen he number n. 5 Guy Lewis Seele, Jr. and Gerald Jay Sussman, "The Ar of he Inerpreer, or he Modulariy Complex (Pars Zero, One, and Two)," MIT AI Lab Memo 453, May 1978. 11
Noes In ranslaing McCarhy's noaion inorunningcodeiriedochange as lile as possible. I was emped o make he code easier o read, bu I waned o keep he avor of he original. In McCarhy's paper, falsiy is represened by f, no he empy lis. Iused o represen falsiy so ha he examples would work in Common Lisp. The code nowhere depends on falsiy happening also o be he empy lis nohing is ever consed ono he resul reurned by a predicae. I skipped building liss ou of doed pairs, because you don' need hem o undersand eval. I also skipped menioning apply, houghiwas apply (a very early form of i, whose main purpose was o quoe argumens) ha McCarhy called he universal funcion in 1960 eval was hen jus a subrouine ha apply called o do all he work. I dened lis and he cxrs as abbreviaions because ha's how McCarhy did i. In fac he cxrs could all have been dened as ordinary funcions. So could lis if we modied eval, as we easily could, o le funcions ake any number of argumens. McCarhy's paper only had ve primiive operaors. He used cond and quoe bu may have hough of hem as par of his mealanguage. He likewise didn' dene he logical operaors and and no, bu his is less of a problem because adequae versions can be dened as funcions. In he deniion of eval. we called oher funcions like pair. and assoc., bu any call o one of he funcions we dened in erms of he primiive operaors could be replaced by a call o eval.. Tha is, (assoc. (car e) a) could have been wrien as (eval. '((label assoc. (lambda (x y) (cond ((eq (caar y) x) (cadar y)) (' (assoc. x (cdr y)))))) (car e) a) (cons (lis 'e e) (cons (lis 'a a) a))) There was a small bug in McCarhy's eval. Line 16 was (equivalen o) (evlis. (cdr e) a) insead of jus (cdr e), which caused he argumens in a call o a named funcion o be evaluaed wice. This suggess ha his descripion of eval had no ye been implemened in IBM 704 machine language when he paper was submied. I also shows how hard i is o be sure of he correcness of any lengh of program wihou rying o run i. I encounered one oher problem in McCarhy's code. Afer giving he definiion of eval he goes on o give some examples of higher-order funcions funcions ha ake oher funcions as argumens. He denes maplis: 12
(label maplis (lambda (x f) (cond ((null x) ') (' (cons (f x) (maplis (cdr x) f)))))) hen uses i o wrie a simple funcion diff for symbolic diereniaion. Bu diff passes maplis a funcion ha uses x as a parameer, and he reference o i is capured by he parameer x wihin maplis. 6 I's an eloquen esimony o he dangers of dynamic scope ha even he very rs example of higher-order Lisp funcions was broken because of i. I may be ha McCarhy was no fully aware of he implicaions of dynamic scope in 1960. Dynamic scope remained in Lisp implemenaions for a surprisingly long ime unil Sussman and Seele developed Scheme in 1975. Lexical scope does no complicae he deniion of eval very much, bu i maymake compilers harder o wrie. 6 Presen day Lisp programmers would use mapcar insead of maplis here. This example does clear up one mysery: why maplis is in Common Lisp a all. I was he original mapping funcion, and mapcar a laer addiion. 13