This article has been a fascinating read, by the way. Kudos to the maintainer of the Gist post. I am also sharing these corrections as comments on the Gist post.
EDIT #1: Downloaded a copy of the original Scientific American article from https://www.jstor.org/stable/24968822 and confirmed that indeed the functions "oval" and "snot" are misspellings of "eval" and "snoc".
EDIT #2: Fixed typo in this comment highlighted by @fuzztester below.
This article simply reinforces that the primary problem with the popularity of Lisp was people explaining Lisp.
This article, like every other Lisp article, tells pre-teen me nothing that he could use. Nobody ever demonstrated how much easier task X is in Lisp over asm/C/Pascal/etc.
By contrast, current me could have told pre-teen me "Hey, that spell checker that took you 7 months to write in assembly? Yeah, it's damn near trivial in Lisp on a microcomputer with bank switched memory that nobody every knew how to utilize (it makes garbage collection completely deterministic even on a woefully underpowered CPU). Watch."
I want to weep over the time I wasted doing programming with the equivalent of tweezers, rice grains and glue because every Lisp article and textbook repeated the same worn out lists, recursion and AI crap without ever demonstrating how to do anything useful.
I just love his writing so much -- he captures what I felt when I discovered Lisp. As a kid learning programming in the 80s, I had already done some BASIC, Fortran, Pascal and COBOL in high school and early college. There were differences, of course, but they had some fundamental commonality.
At UC Berkeley, however, the first computer science class was taught in Scheme (a dialect of Lisp)...and it absolutely blew me away. Hofstadter is right: it feels the closest to math (reminding me a ton of my math theory classes). It was the first beautiful language I discovered.
(edit: I forgot to paste in the quote I loved!)
"...Lisp and Algol, are built around a kernel that seems as natural as a branch of mathematics. The kernel of Lisp has a crystalline purity that not only appeals to the esthetic sense, but also makes Lisp a far more flexible language than most others."
At the risk of diverging off from the original post, I also think that calling it "math" might make things a bit murky (and this is coming from someone who wanted to be algebraic topologist!)
It _is_ an elegant and minimal expression of a style of programming that is ubiquitous among dynamically-typed, garbage-collected languages. And it's a "theory" in the sense that it seems complete, and that you can think of ways to solve problems into Scheme and translate that into other dynamically-typed languages and still end with an elegant solution. Emphasis on the elegant (since minimal, wart-free, consistent and orthogonal, etc.).
Scheme was a simplification and a "cleaning up" compared to conventional Lisps of the time (lexical scoping, single shared namespace for functions and variables etc.)
> IN previous columns I have written quite often about the field of artificial intelligence - the search for ways to program computers so that they might come to behave with flexibility, common sense, insight, creativity, self awareness, humor, and so on.
This is very amusing to me because it reads like a list of things LLMs truly stink at. Though at least they finally represent some nonzero amount of movement in that direction.
You must interact with more interesting people than I because to me LLMs have demonstrated as much "common sense, insight, creativity, self awareness, humor" as the average person I run into (actually maybe more but that makes me sound crazy to myself).
This article, and the two companion articles it mentions, can be found in the book "Metamagical Themas" [0] in chapters 17-19, as well as all of his other articles that appeared in this series of Scientific American.
(the book's title is the article series, which originated as an anagram of the article series that Martin Gardner authored, "Mathematical Games," also published in Scientific American and which Hofstadter then took over)
> Attempting to take the car or cdr of nil causes (or should cause) the Lisp genie to cough out an error message, just as attempting to divide by zero should evoke an error message.
Interestingly, this is no longer the case. Modern Lisps now evaluate (car nil) and (cdr nil) to nil. In the original Lisp defined by John McCarthy, indeed CAR and CDR were undefined for NIL. Quoting from <https://dl.acm.org/doi/pdf/10.1145/367177.367199>:
> Here NIL is an atomic symbol used to terminate lists.
> car [x] is defined if and only if x is not atomic.
> Function: car cons-cell ... As a special case, if cons-cell is nil, this function returns nil. Therefore, any list is a valid argument. An error is signaled if the argument is not a cons cell or nil.
> Function: cdr cons-cell ... As a special case, if cons-cell is nil, this function returns nil; therefore, any list is a valid argument. An error is signaled if the argument is not a cons cell or nil.
I was curious what it is like on Maclisp. Here is a complete telnet session with Lars Brinkhoff's public ITS:
$ telnet its.pdp10.se 10003
Trying 88.99.191.74...
Connected to pdp10.se.
Escape character is '^]'.
Connected to the KA-10 simulator MTY device, line 0
^Z
TT ITS.1652. DDT.1548.
TTY 21
3. Lusers, Fair Share = 99%
Welcome to ITS!
For brief information, type ?
For a list of colon commands, type :? and press Enter.
For the full info system, type :INFO and Enter.
Happy hacking!
:LOGIN SUSAM
TT: SUSAM; SUSAM MAIL - NON-EXISTENT DIRECTORY
:LISP
LISP 2156
Alloc? n
*
(status lispversion)
/2156
(car nil)
NIL
(cdr nil)
NIL
^Z
50107) XCT 11 :LOGOUT
TT ITS 1652 Console 21 Free. 19:55:07
^]
telnet> ^D Connection closed.
$
The use of car and cdr are such a surprisingly concrete implementation detail in the birth of a language that was designed to be mathematical. The most basic and famous operators of "List Processor" were created to operate not on lists but on conses, an element in a particular machine representation that Lisp uses to build data structures! Not only are conses not always interpreted as lists, but a very very important list, the base case for recursive functions on lists, is not represented by a cons.
Sixty years later, most Lisp programs are still full of operations on conses. A more accurate name for the language would be "Cons Processor!" It's a reminder that Lisp was born in an era when a language and its implementation had to fit hand in glove. I think that makes the achievement of grounding a computer language in mathematical logic all the more remarkable.
I certainly know the Lisp information in this article already, but it's still a fun read. Hofstadter just has a charming way with words.
I found this bit extra amusing:
>It would be nice as well as useful if we could create an inverse operation to readers-digest-condensed-version called rejoyce that, given any two words, would create a novel beginning and ending with them, respectively - and such that James Joyce would have written it (had he thought of it). Thus execution of the Lisp statement (rejoyce 'Stately 'Yes) would result in the Lisp genie generating from scratch the entire novel Ulysses. Writing this function is left as an exercise for the reader.
It took a while, but we got there. I don't think 2024's AI is quite what he had in mind in 1983, but you have to admit that reproducing text given a little seeding is a task that quite suits the AI of today.
I do think LISP remains the major language that can encompass the strange loop idea he explored in his work. I know LISP is not the only homoiconic language, but it is the biggest that people know how to use where the "eval" function doesn't take in a string that has to be parsed.
I hate that people are convinced LISP == functional programming, writ large. Not that I dislike functional programming, but the symbolic nature of it is far more interesting to me. And it amuses me to no end that I can easily make a section of code that is driven by (go tag) sections, such that I can get GOTO programming in it very easily.
Another (properly functional) homoiconic language that enjoyed mainstream adoption briefly in '00s is XSLT. Its metaprogramming features were rather widely used, that is, producing an XSLT from XSLT and maybe some more XML, instead of hand-coding something repetitive, was rather normal.
The syntax was a bigger problem than Lisp's syntax, though.
It's not easy to produce a language with a syntax that's good as daily use syntax, but is also not unwieldy as an AST. Lisp is one of the few relatively successful examples.
Not to mention specifically with Scheme and continuation-oriented programming, the line between functional and non-functional programming becomes so blurry as to become nearly meaningless.
I love and relate to any impassioned plea on SWE esoterica, so this seems like as good of a place as any to ask: What, in practice, is this deep level of "homoiconic" or "symbolic" support used for that Python's functools (https://docs.python.org/3/library/functools.html) doesn't do well? As someone building a completely LISPless symbolic AGI (sacrilege, I know), I've always struggled with this and would love any pointers the experts here have. Is it something to do with Monads? I never did understand Monads...
To make this comment more actionable, my understanding of Python's homoiconic functionality comes down to these methods, more-or-less:
1. Functions that apply other functions to iterables, e.g. filter(), map(), and reduce(). AKA the bread-n-butter of modern day JavaScript.
2. Functions that wrap a group of functions and routes calls accordingly, e.g. @singledispatch.
3. Functions that provide more general control flow or performance conveniences for other functions, e.g. @cache and and partial().
3. Functions that arbitrarily wrap other functions, namely wraps().
Certainly not every language has all these defined in a standard library, but none of them seem that challenging to implement by hand when necessary -- in other words, they basically come down to conviences for calling functions in weird ways. Certainly none of these live up to the glorious descriptions of homoiconic languages in essays like this one, where "self-introspection" is treated as a first class concern.
What would a programmer in 2024 get from LISP that isn't implemented above?
Some of it is because many people's only contact with Lisp is via academia, and the academics who teach it actually don't care about developing anything with Lisp. They use it as a vehicle for concepts, and those concepts typically revolve around functional recursion.
The Scheme language and it surrounding culture are also culprits. Though Scheme isn't functional, it emphasizes pure programming more than its Lisp family predecessors. The basic language provides tail recursive constructs instead of iterative ones, and demands implementations to implement optimized tail calls.
I loved Hofstadter's writing on Lisp in Metamagical Themas and adapted the code in the last article of the series to Clojure for a study group at work, written up here[1].
> As you might expect, the value of the atom 1729 is the integer 1729, and this is permanent. (I am distinguishing here between the atom whose print name or pname is the four-digit string 1729, and the eternal Platonic essence that happens to be the sum of two cubes in two different ways - i.e., the number 1729.)
An atom is something defined in the semantics of lisp and a part of the program, it will be represented as bits in the computer memory and as pixels on the screen. A number is a very general concept with many representations, on of which is as a lisp atom, and another could be a pile of 1729 kiwis. The kiwis and the code both represent the number, but they don't represent each other.
The lisp atom 1729 is like a "constant" in a programming language, representing a particular arrangement of bits in lisp systems. The integer 1729 is a number that, in a mathematical sense, has always existed and will always exist regardless of computer systems.
While some atoms can be assigned values, the atom 1729 cannot be assigned any value other than the number 1729.
> Every computer language has arbitrary features, and most languages are in fact overloaded with them. A few, however, such as Lisp and Algol, are built around a kernel that seems as natural as a branch of mathematics.
Algol? The kernel of Algol seems as natural as a branch of mathematics? Can anyone who has used Algol give their opinion of this statement?
From what I’ve studied, Algol wasn’t designed for typical software development—its main purpose was to give computer scientists a way to describe algorithms with a level of rigor that mirrors mathematical notation.
I did some Algol programming back in the late 80s - when it had mostly been obsoleted by Pascal, Modula, and even C for what we called "structured programming" back then.
I remember it as a likeable, economical, expressive language, without significant warts, and which had clearly been influential by being ahead of its time.
So my guess is that Hofstadter was just referring to its practical elegance - rather than the more theoretical elegance of Lisp.
Hard to say without knowing which version of Algol he is referring to. Algol 68 was very different from Algol 58.
Algol 60 was the first language with lexical scope, while Algol 68 was a kitchen-sink language that (positively) influenced Python and (negatively) influenced Pascal.
It was discovered that the procedure mechanism of Algol 60 was effectively equivalent to the lambda calulus. This insight was written out in a famous paper by Peter Landin, "Correspondence between ALGOL 60 and Church's Lambda-notation: part I"
C is basically Algol with curly braces and pointers. The sentiment expressed there is probably equally applicable to C, or maybe Pascal. Those are often held up today as a minimal example in contrast to Lisp. There is a sort of sparse, warty elegance to the family. Blocks, arrays, if/then, assignment, while loops. What more could you need?
I read that article when it came out, as my parents subscribed to Scientific American. Even though I had learned BASIC and Pascal, the concepts in the article were just way over my head. Also, I had no access (that I was aware of at least) to a machine where I could try my hand at Lisp programming. Alas, I wish I had taken it more seriously.
At least Hofstadter was successful at getting me interested in math beyond high school.
I took M490 “Problem Seminar” (a math class) in 1995 with Dr. Hofstadter - we studied triangles, and the definition of a triangle’s center.
You would think that there is a limited set of “triangle centers” but he showed us (and he had us discover and draw them out using The Geometer's Sketchpad) dozens of ways to find triangle centers and he had notes on hundreds more definitions of triangle centers.
His approach to teaching was fun and made us want to take on challenging problems. :)
Me too. I admire the theory of Lisp, but man, all the Lisp folks going "but don't you get it, the absence of syntax IS the syntax!" don't half get tiring.
For some of us, we can just about handle the simple algebraic infix stuff, and we'll never make that leap to "my god, it's full of CARs".
turning character into symbol seems natural, because then you are reducing your needed function space even more. I'm surprised the original operated on prin1 output, not sure what the logic behind that is. on a lisp machine (zl:explode "foo") gives me '(|"| |f| |o| |o| |"|)
Admirer, not user. So ambitious and gorgeous. Hosted on Common Lisp with full integration, so useful now. I hope more people check it out. The new Shen book is awesome.
[+] [-] susam|1 year ago|reply
EDIT #1: Downloaded a copy of the original Scientific American article from https://www.jstor.org/stable/24968822 and confirmed that indeed the functions "oval" and "snot" are misspellings of "eval" and "snoc".
EDIT #2: Fixed typo in this comment highlighted by @fuzztester below.
[+] [-] bsder|1 year ago|reply
This article, like every other Lisp article, tells pre-teen me nothing that he could use. Nobody ever demonstrated how much easier task X is in Lisp over asm/C/Pascal/etc.
By contrast, current me could have told pre-teen me "Hey, that spell checker that took you 7 months to write in assembly? Yeah, it's damn near trivial in Lisp on a microcomputer with bank switched memory that nobody every knew how to utilize (it makes garbage collection completely deterministic even on a woefully underpowered CPU). Watch."
I want to weep over the time I wasted doing programming with the equivalent of tweezers, rice grains and glue because every Lisp article and textbook repeated the same worn out lists, recursion and AI crap without ever demonstrating how to do anything useful.
[+] [-] oaktowner|1 year ago|reply
At UC Berkeley, however, the first computer science class was taught in Scheme (a dialect of Lisp)...and it absolutely blew me away. Hofstadter is right: it feels the closest to math (reminding me a ton of my math theory classes). It was the first beautiful language I discovered.
(edit: I forgot to paste in the quote I loved!)
"...Lisp and Algol, are built around a kernel that seems as natural as a branch of mathematics. The kernel of Lisp has a crystalline purity that not only appeals to the esthetic sense, but also makes Lisp a far more flexible language than most others."
[+] [-] Jeff_Brown|1 year ago|reply
[+] [-] nxobject|1 year ago|reply
It _is_ an elegant and minimal expression of a style of programming that is ubiquitous among dynamically-typed, garbage-collected languages. And it's a "theory" in the sense that it seems complete, and that you can think of ways to solve problems into Scheme and translate that into other dynamically-typed languages and still end with an elegant solution. Emphasis on the elegant (since minimal, wart-free, consistent and orthogonal, etc.).
Scheme was a simplification and a "cleaning up" compared to conventional Lisps of the time (lexical scoping, single shared namespace for functions and variables etc.)
[+] [-] furyofantares|1 year ago|reply
> February, 1983
> IN previous columns I have written quite often about the field of artificial intelligence - the search for ways to program computers so that they might come to behave with flexibility, common sense, insight, creativity, self awareness, humor, and so on.
This is very amusing to me because it reads like a list of things LLMs truly stink at. Though at least they finally represent some nonzero amount of movement in that direction.
[+] [-] zyklu5|1 year ago|reply
[+] [-] ska|1 year ago|reply
[+] [-] kevindamm|1 year ago|reply
[0]: https://www.goodreads.com/book/show/181239.Metamagical_Thema...
(the book's title is the article series, which originated as an anagram of the article series that Martin Gardner authored, "Mathematical Games," also published in Scientific American and which Hofstadter then took over)
[+] [-] susam|1 year ago|reply
Interestingly, this is no longer the case. Modern Lisps now evaluate (car nil) and (cdr nil) to nil. In the original Lisp defined by John McCarthy, indeed CAR and CDR were undefined for NIL. Quoting from <https://dl.acm.org/doi/pdf/10.1145/367177.367199>:
> Here NIL is an atomic symbol used to terminate lists.
> car [x] is defined if and only if x is not atomic.
> cdr [x] is also defined when x is not atomic.
However, both Common Lisp and Emacs Lisp define (car nil) and (cdr nil) to be nil. Quoting from <https://www.lispworks.com/documentation/HyperSpec/Body/f_car...>:
> If x is a cons, car returns the car of that cons. If x is nil, car returns nil.
> If x is a cons, cdr returns the cdr of that cons. If x is nil, cdr returns nil.
Also, quoting from <https://www.gnu.org/software/emacs/manual/html_node/elisp/Li...>:
> Function: car cons-cell ... As a special case, if cons-cell is nil, this function returns nil. Therefore, any list is a valid argument. An error is signaled if the argument is not a cons cell or nil.
> Function: cdr cons-cell ... As a special case, if cons-cell is nil, this function returns nil; therefore, any list is a valid argument. An error is signaled if the argument is not a cons cell or nil.
[+] [-] susam|1 year ago|reply
[+] [-] dkarl|1 year ago|reply
Sixty years later, most Lisp programs are still full of operations on conses. A more accurate name for the language would be "Cons Processor!" It's a reminder that Lisp was born in an era when a language and its implementation had to fit hand in glove. I think that makes the achievement of grounding a computer language in mathematical logic all the more remarkable.
[+] [-] lisper|1 year ago|reply
Scheme doesn't. Taking the CAR or CDR of nil is an error.
[+] [-] sph|1 year ago|reply
Which is a shame, because I prefer (Guile) Scheme to Common Lisp.
[+] [-] smrq|1 year ago|reply
I found this bit extra amusing:
>It would be nice as well as useful if we could create an inverse operation to readers-digest-condensed-version called rejoyce that, given any two words, would create a novel beginning and ending with them, respectively - and such that James Joyce would have written it (had he thought of it). Thus execution of the Lisp statement (rejoyce 'Stately 'Yes) would result in the Lisp genie generating from scratch the entire novel Ulysses. Writing this function is left as an exercise for the reader.
It took a while, but we got there. I don't think 2024's AI is quite what he had in mind in 1983, but you have to admit that reproducing text given a little seeding is a task that quite suits the AI of today.
[+] [-] taeric|1 year ago|reply
I hate that people are convinced LISP == functional programming, writ large. Not that I dislike functional programming, but the symbolic nature of it is far more interesting to me. And it amuses me to no end that I can easily make a section of code that is driven by (go tag) sections, such that I can get GOTO programming in it very easily.
[+] [-] nine_k|1 year ago|reply
The syntax was a bigger problem than Lisp's syntax, though.
It's not easy to produce a language with a syntax that's good as daily use syntax, but is also not unwieldy as an AST. Lisp is one of the few relatively successful examples.
[+] [-] throwaway19972|1 year ago|reply
[+] [-] bbor|1 year ago|reply
To make this comment more actionable, my understanding of Python's homoiconic functionality comes down to these methods, more-or-less:
1. Functions that apply other functions to iterables, e.g. filter(), map(), and reduce(). AKA the bread-n-butter of modern day JavaScript.
2. Functions that wrap a group of functions and routes calls accordingly, e.g. @singledispatch.
3. Functions that provide more general control flow or performance conveniences for other functions, e.g. @cache and and partial().
3. Functions that arbitrarily wrap other functions, namely wraps().
Certainly not every language has all these defined in a standard library, but none of them seem that challenging to implement by hand when necessary -- in other words, they basically come down to conviences for calling functions in weird ways. Certainly none of these live up to the glorious descriptions of homoiconic languages in essays like this one, where "self-introspection" is treated as a first class concern.
What would a programmer in 2024 get from LISP that isn't implemented above?
[+] [-] kazinator|1 year ago|reply
The Scheme language and it surrounding culture are also culprits. Though Scheme isn't functional, it emphasizes pure programming more than its Lisp family predecessors. The basic language provides tail recursive constructs instead of iterative ones, and demands implementations to implement optimized tail calls.
[+] [-] unknown|1 year ago|reply
[deleted]
[+] [-] unknown|1 year ago|reply
[deleted]
[+] [-] anthk|1 year ago|reply
On Common Lisp too, by defining defalias as a macro:
https://stackoverflow.com/questions/24252539/defining-aliase...
[+] [-] eigenhombre|1 year ago|reply
[1] http://johnj.com/posts/oodles/
edit: clarification
[+] [-] silcoon|1 year ago|reply
[+] [-] unknown|1 year ago|reply
[deleted]
[+] [-] paulddraper|1 year ago|reply
He is? What is the distinguishment he is making?
This writing styling is....interesting.
[+] [-] shrubble|1 year ago|reply
[+] [-] Y_Y|1 year ago|reply
[+] [-] WolfeReader|1 year ago|reply
While some atoms can be assigned values, the atom 1729 cannot be assigned any value other than the number 1729.
[+] [-] susam|1 year ago|reply
> (defalias 'plus #'+)
> (defalias 'quotient #'/)
> (defalias 'times #'*)*
> (defalias 'difference #'-)*
Looks like we also need a defmacro for def that is used much further in the article:
> > (def rac (lambda (lyst) (car (reverse lyst))))
I mean the above example fails in Emacs:
If we want the above example to work, we need to define def like this: Now the previous example, as presented in the article, works fine:[+] [-] AnimalMuppet|1 year ago|reply
Algol? The kernel of Algol seems as natural as a branch of mathematics? Can anyone who has used Algol give their opinion of this statement?
[+] [-] gavindean90|1 year ago|reply
[+] [-] andyjohnson0|1 year ago|reply
I remember it as a likeable, economical, expressive language, without significant warts, and which had clearly been influential by being ahead of its time.
So my guess is that Hofstadter was just referring to its practical elegance - rather than the more theoretical elegance of Lisp.
[+] [-] aidenn0|1 year ago|reply
Algol 60 was the first language with lexical scope, while Algol 68 was a kitchen-sink language that (positively) influenced Python and (negatively) influenced Pascal.
[+] [-] earthicus|1 year ago|reply
https://dl.acm.org/doi/10.1145/363744.363749
[+] [-] retrac|1 year ago|reply
[+] [-] unknown|1 year ago|reply
[deleted]
[+] [-] analog31|1 year ago|reply
At least Hofstadter was successful at getting me interested in math beyond high school.
[+] [-] TruffleLabs|1 year ago|reply
You would think that there is a limited set of “triangle centers” but he showed us (and he had us discover and draw them out using The Geometer's Sketchpad) dozens of ways to find triangle centers and he had notes on hundreds more definitions of triangle centers.
His approach to teaching was fun and made us want to take on challenging problems. :)
[+] [-] lproven|1 year ago|reply
For some of us, we can just about handle the simple algebraic infix stuff, and we'll never make that leap to "my god, it's full of CARs".
https://xkcd.com/224/
[+] [-] eska|1 year ago|reply
Sometimes I wonder what non-programmers think about us when they hear us talk..
[+] [-] timonoko|1 year ago|reply
[+] [-] timonoko|1 year ago|reply
[+] [-] g19205|1 year ago|reply
[+] [-] _yb2s|1 year ago|reply
[+] [-] lopatin|1 year ago|reply
[+] [-] corinroyal|1 year ago|reply
[+] [-] rsktaker|1 year ago|reply
[+] [-] marbletiles|1 year ago|reply