> Technically, a compiler translates the program into an executable form to be run thereafter, while an interpreter figures out what the program says to do and just does it immediately.

Though this isn't wrong per se, it glosses over the fact that the compiler is just translating the program into another language which is then interpreted: that representation is fed to some machine which figures out what the compiled programs says to do and does it immediately.

Therefore, most of this article is basically misguided.

The main differences between compilation and interpretation (particularly of Lisp) are:

* in the interpreted program, there is no difference in representation between a part of the program which quotes itself as a datum, and the unquoted parts that are being interpreted. In the compiled version, a quoted '(if x y z) is still the four-element list, but a (if x y z) form has been translated to some other form, such as the instructions for a VM.

* in the interpreted version, environments, such as the lexical environment, are likely accessed using the same symbols that appear in the source code, so that if the current environment object is made available to the program, it can dynamically inspect it using variable names as keys. In the compiled verison, the representation of environments has changed to something else, which is not necessarily susceptible to such introspection. It might not be a searchable structure at all, and not all bindings visible in the source code are necessarily even in that structure (e.g. variables replaced by constant propagation).

We compile because the representation change is such that the program loads and runs faster. (Also, the translated program is harder to understand and modify, which helps us earn a living. The proprietor who has the source code of the program has a competitive advantage in providing services and future versions of the program to that program's customers, compared to third parties who don't have that source code.)

(The first difference, by the way, doesn't apply to languages that don't resemble Lisp. Since, say, the C language cannot quote a for (;;) {} construct as a symbolic datum, it cannot be in any way sensitive to its representation change between interpreting and compiling.)

The tricks we can perform with fexprs are not easy to replicate under compiling for mainly these reasons. Fexprs are user-coded extensions to the interpreter; if an interpreter is not used, they are not relevant. A fexpr could, each time when it is invoked, read some code from a file or other external input, and evaluate that code in the scope of the lexical variables. But that doesn't mean interpretation is made of self-referential magic that can't be duplicated under compilation. The fexpr has an ready analogy in the compiled model: we could make an extensible virtual machine, where the user is able to define a new kind of instruction whose actions is to fetch some piece of machine code from somewhere and execute it such that it has access to the current environment frame. (What would be additionally needed would be some compiler hook so the user can define a macro which expands to some blurb that makes the compiler generate that new instruction.)

Sure, the custom machine instruction wouldn't be interpreting Lisp source, and accessing Lisp variables. But the complementary fact is true about the fexpr: the fexpr doesn't interpret VM code and doesn't know how to access a compiled stack frame. (With effort, both directions could be bridged to some extent.)