Further work on expansion-passing style?

Expansion-passing style: A general macro mechanism [pdf] was published in 1988. It proposes a system that increases macros' flexibility by allowing them control over the expansion of their subforms.

Since EPS came out at the same time as lots of new work was being done on hygienic macro systems, it seems not to have gotten the attention it deserved. At least I can't find any work based on it. Is anyone aware of subsequent work that I missed? What about any work that, while not overtly related, might address the issues at the end of the paper?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

The Racket macro system

The Racket macro system might be related. See Macros that work together which has also been posted here.

[edit]I have only read the abstract of the EPS paper, though. As far as I can tell Racket uses a more explicit style by providing explicit expansion methods[/edit]

The very most general calling discipline in the world....

The most general calling discipline which can exist is to pass promises as arguments and let the called function decide what to do with them, and pass promises as return values and let the call site decide what to do with them. A promise, as you will recall, is comprised of the expression and the environment in which the expression appeared.

If that were it, and the promises were treated conventionally, it would just be a 'lazy' language. The generality of macrology, etc, comes from allowing the called routine (or the call site in the case of return promises) to *abuse* the promises to varying degrees.

Things you can do with a promise, in order from least to most semantically general/abusive, are:

"Force" -- the conventional treatment of a promise. The expression is evaluated in the provided environment and the promise is replaced with the value returned from it. You can only "Force" a promise once. With forcing, the language is still referentially transparent.

"Evaluate" -- That is, you evaluate the expression in the given environment, but DON'T replace the promise with the result of the evaluation. You can "Evaluate" a promise as many times as you like, so you could write looping forms, etc, without any code transformations. Referential transparency is still preserved.

"Initialize" -- the expression, which is a variable name, is initialized, or given a value in the passed-in environment. This injects a new binding visible at the call site. Initialization as a side effect is a minor and tolerable exception to referential transparency.

"Reassign" -- the expression, which is a variable name, is mutated, or has its value changed, in the passed-in environment. This mutates a binding visible at the call site. Reassignment as a side effect is a major but tolerable exception to referential transparency.

"Hygienic Macrology" -- The called function can arbitrarily transform the expression before evaluating it (or not), as many times as desired, in the environment of the call site, but is prevented from accessing, initializing, or mutating any bindings whose variable names are not given in the argument expression. Referential transparency is not preserved, but at least variable capture is prevented.

"Standard Lispy Macrology" -- The called function can arbitrarily transform the expression before evaluating it (or not) as many times as desired, in the environment of the call site, and is *NOT* prevented from accessing any binding visible, nor from introducing new visible bindings. Variable capture is not prevented.

"Break" -- The most complete abuse is when the called routine can look inside the promise to get direct access to see and manipulate the caller's environment, as well as see and manipulate the expression. This allows arbitrary manipulation of the caller's environment, up to and including capturing it by storing it in a persistent structure, listing all visible bindings, the deletion of existing bindings, etc. The expression, as arbitrarily transformed, may then be executed, as many times as desired, in the caller's environment, in the callee's environment, or in *ANY OTHER ENVIRONMENT* available to the callee (which may include first-class environments stored as values, in variables not even visible at the call site). If functions are allowed to break promises, there is effectively no limitation on what any called function can do -- nor even on whether what it does has anything to do with the arguments you passed it or any environment visible to you.

It's a typical case of things being more problematic the more general they get.

Ray