The Subtleties Of Classic Lisp: Lambda Lists

About a month ago I checked one of my remaining bucket list points: Implement a Lisp.

I've been using Common Lisp for years, and thought that I understood Lisp. However, my implementation experionce showed that I only thought I understood Lisp. I mean I was a pretty effective coder. And few years back I went through 'Let Over Lambda' and became quite proficient at the macro magic. But as Common Lisp is a compiler, in the end, coding in Lisp is not entirely dissimilar to coding in C - plus the interactiveness plus a better macro subsystem.

So as I dove into the implementation of a classic Lisp interpreter, I realized that when you are _not_ compiling, things are different. When you are interpreting anyway, there is a very low cost to treating code as data. You can manipulate code as much as you want prior to evaluation - the interpreter does not care! You can delay evaluating arguments and use full fexprs instead of macros - fexprs were eliminated because they made compilation impossible... You can invoke the evaluator anytime you want.

Classic McCarthian Lisp features very flexible lambda lists. A lambda-list may be a normal list (in which case arguments are bound as expected), a dotted list (where the paramter after the dot picks up all remaining arguments in a list), or it could be a single variable (not a list) which picks up all the arguments.

The bindings created by evaluating the lambda-list are likewise flexible. It is possible to splice lists by assigning a bound varibla that is a list, using the dot syntax. For instance you can extend a list like this:

>((lambda expr (list + 1 2 . expr)) 10 11)
(+ 1 2 10 11)

This demonstrates 1) the lambda-list consisting of a variable that binds anything passed to it after evaluation and 2) the ability of the system to splice the evaluated expression to the end of a list...

Between the two, it is possible to build higher order functions:

>((lambda (f . expr)(lambda args (f . expr))) + 7