💾 Archived View for stack.tilde.cafe › gemlog › 2023-11-18.lispll.gmi captured on 2024-06-16 at 12:19:11. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-12-28)
-=-=-=-=-=-=-
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