Once upon a time that was true. The original Lisp interpreter could be extended by writing new FEXPR routines, which are given their argument code as source to interpret.

In the FEXPR era, Lisp didn't have lexical scopes, so FEXPR routines did not require an environment parameter.

FEXPRs were eventually abandoned in favor of macros. That was largely due to practical reasons; Lisp people were pressuring themselves or else being pressured to make Lisp perform better. It was not because FEXPRs were exhaustively researched.

A modern treatment of FEXPRs will almost certainly be lexically scoped, so that FEXPRs will have a lexical environment parameter to pass to the recursive eval calls.

Autology makes a different choice here by passing a parameter *i* (implicitly) which is not simply the lexical environment, but an object for interpretation, which can be selectively customized. This makes a categorical difference.

It's immediately obvious to me that this allows possibilities that are impossible under FEXPRs.

Here is why: when a FEXPR recursively evaluates code, whether through the standard eval provided by the host Lisp, or its own custom evaluation routine, it will encounter invocations of other FEXPRs. And those FEXPRS are "sealed off": they are coded to invoke a certain eval and that's it.

Concretely, suppose we are writing an if operator as a FEXPR. We get the if form unevaluated, and destructure it into the pieces: condition, consequent, alternative. What our FEXPR does then is recursively call eval to evaluate the condition. If that is true, we recursively call eval to evaluate consequent, otherwise we call eval on the alternative. (In all eval calls we pass down the lexical environment we were given).

Now suppose someone is writing a FEXPR in which they want to heavily customize the Lisp dialect with their custom evaluator, custom-eval. Problem is what, happens when custom-eval sees an if form that is programmed using our if FEXPR? It just calls that FEXPR, whose body calls eval; it doesn't know that the code is in a customized dialect which requires custom-eval!

It looks as if that the approach in Autology handles (or if not, could easily handle) the situation. The FEXPR which customizes the Lisp dialect can pass down a modified interpreter via *i*. Then when interpretation encounters if, it will call the if FEXPR, and what FEXPR will use the customized interpreter on the condition, then and alternative.

Now (non-hygienic) macros can already support this, in theory. If you've written a DSL as a macro, and in that DSL you choose to expand regular macros defined in the host Lisp, at least some of them with simple behaviors like the if example will also Just Work. This is because macros don't (usually!) call eval, but generate code. That code just ends up being pasted into the expression of whatever dialect is being processed, and will therefore be interpreted as that dialect. A macro arranges for evaluation by inserting the pieces of code into the template such that they are in the right kind of syntactic context to be evaluated. For this to work across dialects, the dialects have to be similar. E.g. an if macro might rewrite to cond. If the target dialect has a sufficiently compatible cond construct, then everything is golden: the if to cond rewrite will work fine.

So how we can look at dynamic interpreter customization is that it brings one aspect of FEXPRs closer to macros. Macros can do some things that FEXPRs cannot, and vice versa.