William Byrd on ”The Most Beautiful Program Ever Written” (2017) [video]
youtube.com
youtube.com
Comparing it to Maxwell's equations sharpens this. The equations are deeply illuminative of fundametal processes of reality, summarizing comprehensively and precisely decades of exacting experimentation by the best physicists of the age. The speed of light (in vacuum, and in any medium) is simply derived from them. No detail of them could be deduced without abstraction from all the experience of all who came before.
How much work went into this program? Would any two people, who just knew Lisp, but no theory of computation, asked to write it, write it differently? How long could it take? An hour, a week, a month? How much insight would it take? How much lived experience does it represent?
Think it through: A Lisp interpreter in Lisp. It demonstrates that, if you are handed a Lisp runtime, as we are today, it is easy to make it execute Lisp programs. With small changes, it runs slightly different programs.
Should that impress us? Where did this Lisp runtime come from, and why would one expect it to be tuned for anything but running Lisp programs? If it were not good for running Lisp programs, what would it be good for?
The program relies, fundamentally, on all the science (and, frankly, engineering) that came before, but illuminates none of it. It is a fleck of foam that rides determinedly at the surface, belying the depths that support it. It illuminates nothing beyond itself.
In this case, it's running a program which very itself easily runs Lisp programs given to it as a source code data structure. That we normally don't expect. For instance, in C language run-time, though it of course supports running compiled C programs, it doesn't provide a straightforward way for one of these programs to itself process C programs. A C program that interprets a decent subset of C is pretty complicated. It has to provide a number of data structures, of course a parser (because C programs don't have a data structural representation) and memory management for the parse time and run-time and so on. The C-processing C program can't even ask for a function by name so that it could resolve a function call out to the native run-time. This is possible with some operating system extensions (dynamic symbols). That just solves the name resolution problem: then there is the problem of dynamically constructing the argument list for a function call and executing it. This requires a library not supplied in the C language such as GNU ffcall, or libffi. Think about it: a C interpreter for C needs to use a FFI mechanism (first F == foreign!) to expose the C run time to the interpreted C, and that FFI is far from a standard language feature. Lisp just resolves a symbol and calls apply and that's it; nothing is foreign: no visas have to be issued, no passports are stamped.
In Lisp, everything is still there. "Resolves a symbol" relies on the runtime. "Calls apply" relies on the runtime. You don't need to write those, because you inherited them.
apply is just a function in Lisp. The host Lisp itself doesn't (necessarily) use apply for calling functions. When an expression like (cons a b) in the host Lisp is processed, it's quite likely compiled into machine code or byte code that doesn't use apply.
The hosted interpreter uses apply because it operates at the host Lisp's run-time; with respect to that host, it's handling function calls by dynamically constructing lists of evaluated arguments and then having to solve the problem of applying a list of arguments to a function.
A C run-time could provide apply. Libraries exist for this like GNU ffcall, and libffi. They are low-level, hard to use and error-prone compared to apply.
A C run-time can provide a way to get the addresses of functions also; we can do this in modern POSIX environments with dlsym, in Win32 with GetProcAddress and such.
If we put together libffi and dlsym to call a function, and make the slightest mistake, we have corruption or a crash on our hands. Whereas apply is robust; we get diagnostics about too many or too few arguments and so on.
If ISO C provided these features, then we could have ISO C "C-in-C" meta-circular interpreters. They would contain a significant lines-of-code expenditure compared to Lisp-in-Lisp.
(SF) If nothing else, Lisp was carefully defined in terms of Lisp.
(AK) Yes, that was the big revelation to me when I was in
graduate school—when I finally understood that the half
page of code on the bottom of page 13 of the Lisp 1.5
manual was Lisp in itself. These were “Maxwell’s
Equations of Software!” This is the whole world of
programming in a few lines that I can put my hand over.
https://queue.acm.org/detail.cfm?id=1039523(SF) If nothing else, Lisp was carefully defined in terms of Lisp.
(AK) Yes, that was the big revelation to me when I was in graduate school—when I finally understood that the half page of code on the bottom of page 13 of the Lisp 1.5 manual was Lisp in itself. These were “Maxwell’s Equations of Software!” This is the whole world of programming in a few lines that I can put my hand over.
Page 13 of that manual is page 21 of this PDF: http://www.softwarepreservation.org/projects/LISP/book/LISP%...
You could just throw the interpreter away and have a compiler only (bootstrapped with the compiled version of itself), but then it's useful to have two models of execution for programs.