From: Drew McDermott
Subject: "Serialization" in macro expansion
Date: 
Message-ID: <39732BAB.F8807AF0@yale.edu>
This message is about an intricate issue of interest only to the truly
dedicated.

What exactly are the rules on what macros can expand into?

Consider the following test file:

(in-package "USER")

(declaim (special car* pal* virt*))

(defmacro weird (s x)
  `(setq ,s '(,(eval x) ,(eval x))))

(weird car* #'car)

(eval-when (:compile-toplevel :load-toplevel)
   (defun pal (l) (append (reverse l) l)))

(weird pal* #'pal)

(weird virt* #'foo)

--------

This doesn't do anything useful.  However, it's pretty clear what it's
supposed to do in the
case of (weird pal* ...), namely, set the global variable pal* to the
value (list #'pal #'pal),
an object such that (funcall (car pal*) '(a b c)) => (c b a a b c).

In the case of (weird virt* ...), the value obviously depends on the
compile-time definition
of foo.  If you define foo to be (lambda (x) (list x x)), then virt*
should be set to the
equivalent of (list #'(lambda (x) (list x x)) #'(lambda (x) (list x
x))).  However, if foo is a
compiled function, and we compile this file, the fasl file must contain
the code for foo, even
if it is loaded into an environment for which foo is not defined.

Here's is another example:

(in-package "USER")

(declaim (special ying* yang* xxx*))

(defmacro strange (s x)
  `(setq ,s '(,(eval x))))

(strange ying* xxx*)

(strange yang* xxx*)

---------

Here the compile-time value of the special variable xxx* must be
inserted into the fasl file.
If the fasl file is loaded into a lisp in which xxx* is not defined, it
should still set ying* and
yang* to lists whose cars are eq.

To my surprise, Allegro Common Lisp handles both these cases
"correctly."  That is, if
you load the fasl files into a fresh lisp, virt* is a list containing
definitions of foo, even
though foo isn't defined, and ying* and yang* are non-eq lists
containing eq elements.
Is this because the implementors of Allegro are incredible hackers, or
because the
definition of  Lisp requires this behavior?

Stop press: Allegro doesn't get it completely right.  If foo is defined
(at compile time) as
a recursive function, e.g.,
(defun foo (l)
   (cond ((null l) '())
             (t (cons (car l)
                          (cons (car l)
                                   (foo (cdr l)))))))
then if foo is compiled the whole thing is reconstructed properly when
the fasl file is
loaded.  But if foo is interpreted the recursive call causes an
"undefined function" error.
I can make a case for either of these interpretations, but they can't
both be right!  So this
is a case where compiling changes the meaning of a function, which is a
no-no, I assume.

I use the word "serialization" in the subject line of this message
because it seems as if
doing the right thing in this case requires the ability to serialize any
object into a fasl file, in
the Java sense of the word.

I use eval in the toy examples, but the issue has nothing to do with
eval; any computation
that inserts a pointer to a non-S-expression into a structure that is
the result of a macro
expansion will illustrate the point.

     -- Drew McDermott

From: Kent M Pitman
Subject: Re: "Serialization" in macro expansion
Date: 
Message-ID: <sfwd7kcmwio.fsf@world.std.com>
Drew McDermott <··············@yale.edu> writes:

> This message is about an intricate issue of interest only to the truly
> dedicated.
> 
> What exactly are the rules on what macros can expand into? [...]

Hi, Drew.

I didn't have time to think through the details of what you said, but the 
answer is spelled out in the language spec.

The things to keep in mind are that the rules for what macros can expand into
are not the only rules in play here.  Macros can expand into things which
cannot be externalized into files.  If you use a macro in a situation where
the result of the expansion will be externalized into a file; that is, if you
file compile one of the macros in question, then you need to intersect the
rules for what macros can expand into (which is more or less anything that
EVAL can handle) and the rules for what can be externalized into files (which
is much narrower).

See 3.2.4 Literal Objects in Compiled Files in the Common Lisp HyperSpec.
I don't like to post bookpark pointers into other than the front page, but
it's easy to get to 3.2.4 by going through the chapter index at:

  http://www.xanalys.com/software_tools/reference/HyperSpec/FrontMatter/index.html

Pay particular attention to the stuff about Externalizable Objects in 3.2.4.1,
which is really what you're asking about.

You may also want 3.2.3 File Compilation, and perhaps some other sections.

There are also some "gotchas" in 3.2.2.3 "Semantic Constraints" under the
compilation semantics section.  Be sure to check these.

I know this isn't a full answer to your question, but I hope it gets you
going.
 --Kent
From: Pekka P. Pirinen
Subject: Re: "Serialization" in macro expansion
Date: 
Message-ID: <ixem4r2r8g.fsf@harlequin.co.uk>
Drew McDermott <··············@yale.edu> writes:
> (defmacro weird (s x)
>   `(setq ,s '(,(eval x) ,(eval x))))
>
> ;;; [some omitted]
> (weird virt* #'foo)
>
> [...] if you load the fasl files into a fresh lisp, virt* is a list
> containing definitions of foo, even though foo isn't defined, and
> ying* and yang* are non-eq lists containing eq elements.  Is this
> because the implementors of Allegro are incredible hackers, or
> because the definition of Lisp requires this behavior?

It's a happy accident.  Functions are not externalizable objects (see
Kent's reply) in general, but a compiler has to have some abilities to
write functions into fasl files, in order to compile DEFUNs.  In this
case, it has helpfully used these abilities to externalize #'FOO.  I
bet it wouldn't work if FOO was a closure.

> Stop press: Allegro doesn't get it completely right.  If foo is
> defined (at compile time) as a recursive function, [...] then if
> foo is compiled the whole thing is reconstructed properly when the
> fasl file is loaded.  But if foo is interpreted the recursive call
> causes an "undefined function" error.  I can make a case for either
> of these interpretations, but they can't both be right!  So this is
> a case where compiling changes the meaning of a function

No, it's a case where the meaning is vague and the interpreter and the
compiler pick different interpretations, both of them right.

The recursive call refers to a function called FOO, and since there is
none, it is correct to signal an error.  However, according to ANS
section 3.2.2.3 "the compiler may (but is not required to) assume that
an apparent recursive call to a function named F refers to the same
definition of F, unless that function has been declared NOTINLINE".
So calling the same function object (which is what you evidently mean
by "properly") is legal.  It's a bit wierd that a similar license is
not given to the interpreter, but since EVAL may actually compile and
"compile" is not defined very strictly, what you think of as the
interpreter is not barred from assuming that.

As you see, the vagueness is explicit in "may (but is not required
to)".
-- 
Pekka P. Pirinen, Adaptive Memory Management Group, Harlequin Limited
Mail should be private, whether on paper or on disk.  Public gatherings
should be a right, whether virtual or in person.