Previous page Table of Contents Next page
PLOT macros are "hygienic," which means that they provide referential transparency. That means that when a name in the expansion of a macro came from the macro call, the name refers to the same definition in the expansion as it would at the call site. When a name in the expansion of a macro came from the macro definition, the name refers to the same definition in the expansion as it would at the macro definition site. Each name is evaluated in the context appropriate to its place in the original source code before macro expansion. The purpose of referential transparency (or hygiene) is to prevent the meaning of programs from being changed by accidental name clashes where a macro and its caller both happen to choose the same name for different things. Thus local definitions introduced or referenced by macros are independent of local definitions introduced or referenced by callers of macros. Furthermore, free variables in the expansion of a macro placed there by the macro itself refer to definitions in the scope where the macro was defined, not in the scope where it was called.
When a macro expansion contains a call to another macro, more than two contexts are in play. A name can be in the context of the original macro call, in the context of the definition of the first macro, or in the context of the definition of the other macro.
When macro-defining macros are used, more than two contexts are also in play. A name can be in the context of the final macro call, in the context of the call to the macro-defining macro which is a macro definition site, or in the context of the site where the macro-defining macro was defined.
It is not as simple as that when a macro expansion introduces local definitions. A local definition can be invisible, visible, or anaphoric.
An invisible local definition is one that a macro introduces for its own purposes. Names in the context of the macro call should not be affected by this definition. This is accomplished by naming the definition with a name in a unique context that is created fresh for each macro invocation. A typical example would be the or infix macro, which defines temp to the value of the left-hand side so it can reference it twice, once to test it against false and again to return it.
A visible local definition is one that a macro introduces for a purpose that is part of the external interface of the macro. Names in the context of the macro call should be affected by this definition. This is accomplished by naming the definition with a name in the context of the macro call site which is supplied as part of the macro call. A typical example would be the for macro, which defines iteration variables named in the clauses of the macro call.
An anaphoric local definition is the same as a visible local definition except that the name to use is a convention of the macro rather than being supplied as part of the macro call. This is accomplished by constructing a name with a known spelling and the context of the macro call site. A typical example would be the for macro, which defines local macros collect, sum, etc. as anaphoric local definitions.
Those are the requirements. The implementation is:
The contexts necessary to implement referential transparency are represented by instances of the class macro-context. The identity under eq of a context allows definitions to be visible or invisible. When looking up a name in a local scope, a definition is visible if the definition's name has the same spelling (case-independent string equality) and has the same context (eq). When no definition is found in any of the current nested local scopes, and the name's context is a macro-context, lookup ignores the current global scope and continues looking up the name stripped of its context in the scope contained in a slot of the macro-context. This is the scope where the macro was defined.
The pseudo-function unique-macro-context returns a newly created macro-context that captures the local scope in which the function call was compiled. This is implemented by defining unique-macro-context as a macro with the syntax of a function call.
The function get-previous-context returns the appropriate context for anaphora. This is the context of the name of the macro in the macro call.
A module can also be used in place of a macro-context object as the context of a name and and definition lookup goes directly to that module.
The value false can be used as a context. When a definition is not found in the current local scope, look for a global definition in the current module.
Every name in the source code model carries a context, which is false, a macro-context, or a module. Names that appear directly in source code have false context, or a module when the name-in-module lexical syntax is used. Names with a macro-context are only created by macros. Each macro call creates a new, unique macro-context object whose referenced scope is that of the macro definition site.
Creating a global definition named by a name-in-context simply creates a global definition in the module that is the outermost scope containing the context. The name of the definition is the simple-name with the same spelling as the name-in-context.
Can a name-in-context in a local macro capture the local definitions in scope where the macro was defined? Yes, but it is an error to reference such local definitions other than macros and constants, when they are not accessible. A name-in-context can only reference a local definition created by the macro expansion, or a macro or constant locally defined in a scope containing the macro definition, or a global definition in the module current when the macro was defined, if the macro is called from outside the scope where it was defined.
It is fairly easy to see that this mechanism is sufficient to make macros hygienic without being verbose or getting in the way.
Previous page Table of Contents Next page