On 2 May 2007, at 22:02, Geoff Wozniak wrote:
> I'm looking at defining a function that can be used to identify
> lexical context using environment objects passed into compiler
> macros. Here's an outline of what I want to do.
> ;; File A
> (defun example () ...)
> (defun foo () ... (example) ...)
> ;; File B
> (defvar *environments* nil)
> (define-compiler-macro example (... &environment env)
> (push env *environments*)
> Assume that example only appears in foo. Suppose we define the
> compiler macro for example and then re-compile file A. foo is
> executed at some point, but the source code is unchanged. We then
> compile file A again. *environments* should contain two
> environment objects.
> How can I go about defining some sort of equality relation on these
> environment objects? eq/eql/equal obviously won't work and equalp
> results in a stack overflow (which isn't terribly surprising). In
> essence, I want to be able to differentiate calls to a function
> with respect to lexical context. Thus, the call to example in
> (defun bar () ... (example) ...)
> would be different than the call above in foo. I also need to
> differentiate between two calls like this:
> (defun baz ()
> After a cursory look at environment objects, it looks like this
> might be possible (although I'm certain about the last case).
> Nevertheless, I'd appreciate any help that could steer me in the
> right direction, employing the source code or not. I'd rather not
> sit spinning my wheels in some dark corner of the distribution when
> I know there are people out there considerably more knowledgeable
> about environment objects than myself.
What you want is not directly possible, not with environment objects
as such - at least not if you want to stay portable.
There are two reasons for that:
- Section 184.108.40.206 of the HyperSpec states that the nature of the
implementation of environment objects is implementation-dependent.
This means that you may not even rely on object identity.
- What's even more important, Section 3.4.4 states that the objects
bound to environment parameters have dynamic extent. This means that
the information an environment object contains may not be reliable
anymore after return from a macro function. (For example,
implementations may decide to reuse or side effect environment object
as they wish.)
However, there is no reason to despair because you can pass along all
kinds of information about the lexical environment in your own
format. There are two ingredients that you can use here:
- A macro can return anything, it doesn't have to be lists or symbols.
- The definition of a macro sees all the lexically surrounding
macros. The body of a macro is actually affected by the outer macro
definitions. For example, the following code snippet works:
(macrolet ((foo () 42))
(macrolet ((bar () (foo)))
Since a local macro is expanded in an environment that contains the
outer macros, it is possible to access that information
(macrolet ((foo () 42))
(macrolet ((bar (&environment env)
(if (eql (macroexpand '(foo) env) 42) t
So especially, it is possible to shadow defun and replace it with
your own defun that expands into code that carries the necessary
information as return values in some internal local macros. You can
then also ensure that that information has indefinite extent and has
a reasonable equivalence predicate, etc.
(I have used this technique to implement a hygienic macro system for
Common Lisp in completely portable code - still to be published... ;)
It's also very helpful to carefully study the example code in
COMPILER-LET-CONFUSION in the HyperSpec. (In my own code, I prefer
symbol-macrolet to macrolet for passing along information because the
code seems more elegant that way - but that's subjective, of course.)
I hope this helps.
Pascal Costanza, mailto:pc@..., http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium