This is a similar case. You'll learn how to understand how a Lisp interpreter works. You'll get an extremely simple one, which is written in itself. The same primitive language which is interpreted is used to write the interpreter. That's basically the local maximum of simplicity. You'll reuse much of Lisp, including the data structures, I/O, memory management, etc. But you can concentrate on the core of the interpreter: how does it process lists of code to compute a result. Thus you'll see a minimal language applied to itself. The minimal language can be written in very primitive list processing operations in a few lines of code.
I've taken one of McCarthy's example interpreters and made the language Common Lisp compatible. One can then load the interpreter into Common Lisp and run example programs. Which example programs could we run? We can run the interpreter itself. Then we have an interpreter of the small Lisp, which runs itself and which itself is running in a larger, but compatible, Lisp.
https://gist.github.com/lispm/d752d5761f7078de4041d4e453e70c...
When you run this Lisp in Lisp, you can debug itself, while it is running in itself... which you can then debug, too.
Once you understand how this simple interpreter works, you can then start thinking of changing it: tweaking the implemented language, making it faster, implement a larger language, ...
The main point by Lisp-ers is that the language is just code, and a Lisp program can be modified using the same techniques as you would any other code, and it's easy to do - much easier than almost any other language of the time.
Definitely easier than implementing Python-in-Python is now.
Why would you need it? I believe part of it comes from the idea that you should create a domain-specific way to express your problem, letting the solution naturally fall out. That might not be Lisp, but something that can be converted to Lisp (eg, through a macro) or interpreted by Lisp. If you know how to make a Lisp, you know how to make an interpreter for many other small languages.
Not that my opinion carries much weight, but, I dislike the "Lisp can be written in Lisp" argument because I think it's cheating to re-use the garbage collection of the underlying Lisp implementation.
The near-contemporary languages FORTRAN and Algol were not defined in themselves. Exhibiting (for example) an implementation of FORTRAN in FORTRAN would not be enlightening - it would be this big complicated thing that would be much harder to understand than Lisp in Lisp.
- F# implemented futures (promises) in F#. So any F# platform could immediately start using futures without having to upgrade the language runtime. I think C# and JS were able to polyfill promises, but the simpler async/await syntax could not be polyfilled. (It might be possible with transpilers like Babel.)
- In a similar fashion, some JS features were implemented as polyfills to support browsers that lacked support.
As others comments have mentioned, the big take-away from "Lisp can be written in Lisp" is the concept that data and code are the same. Code modifies data. Code is just data, so code can be used to generate new code.
One of the practical applications is you can write a domain-specific language (DSL). Not only can you write "Lisp in Lisp," you can also write specialized languages made to perfectly handle your use case.
I believe Paul Graham made a DSL for e-commerce sites which helped him stay ahead of the competition:
1. His DSL was at least one level of abstraction higher than what everyone else was dealing with.
2. Lisp's recursive/tree structure was a good match with the structure of HTML documents.
Of course, functional programming is just a way of thinking; it can be implemented in non-functional languages; FP languages just make it easier.
I forgot where I learned all this, but this article seems to explain in more detail: https://hw.leftium.com/#/item/606619
It also basically requires that there is very little that the language can do, that you could not also do. Which is in stark contrast to a lot of languages.