Message from discussion apparently undefined function called by macro
From: Pascal Costanza <p...@p-cos.net>
Subject: Re: apparently undefined function called by macro
Date: Tue, 27 Jun 2006 23:22:32 +0200
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
X-Trace: individual.net sU2YB2rYr+Qo96YYGaFrkArqnlZnjN1UX+Ddp+lBEpk+p4vrcH
User-Agent: Thunderbird 22.214.171.124 (Macintosh/20060530)
Greg Bacon wrote:
> ;(eval-when (:load-toplevel :compile-toplevel :execute)
> (defun id (x) x)
> (defmacro define-foo-class (name slots)
> `(defclass ,name ()
> ,(mapcar #'id slots)))
> (define-foo-class bar
> ((baz :initarg :baz :accessor baz)
> (quux :initarg :quux :accessor quux)))
> If I try to load it in a fresh image, clisp complains about undefined id:
> > (asdf:oos 'asdf:load-op :foo)
> ; loading system definition from foo.asd into #<PACKAGE ASDF0>
> ;; Loading file foo.asd ...
> ; registering #<SYSTEM FOO #x102A2839> as FOO
> ;; Loaded file foo.asd
> ;; Compiling file /tmp/asdf/packages.lisp ...
> ;; Wrote file /tmp/asdf/packages.fas
> ;; Loading file /tmp/asdf/packages.fas ...
> ;; Loaded file /tmp/asdf/packages.fas
> ;; Compiling file /tmp/asdf/foo.lisp ...
> *** - FUNCTION: undefined function ID
> Loading by hand, i.e., (progn (load "packages.lisp") (load "foo.lisp")),
> is no problem, and as you can guess from the commented lines, wrapping
> ID in EVAL-WHEN is also a workaround.
> Well, hmm, my misunderstanding is more basic than ASDF or even the
> reader. After removing the initial IN-PACKAGE form from foo.lisp,
> COMPILE-FILE still complains about undefined ID.
This isn't related to packages or systems (only in the sense that
systems can help you to solve this problem - see below).
> Why is ID undefined? Please help me correct the error in my mental
Common Lisp distinguishes between interpretation and compilation. When
code is interpreted, each top-level form is interpreted after another,
which means that each form can rely on all effects being "correctly"
produced by the previous forms. That is, a defun defines a function,
defmacro defines a macro, a defvar defines a variable, and so on.
When code is compiled, this is not necessarily the case. It is the case
that each form is processed by the compiler, and for example it is
guaranteed that all macros are completely expanded and thus "removed"
from the code, but it is not necessarily the case that the effects are
produced. Typically, the effects will only be produced at runtime. So,
for example, a defun only announces the presence of a function at
compile-time (so that other parts of the code can be checked, for
example whether they pass the right number of arguments, or whether you
accidentally redefine a function somewhere else), but the function
itself will not be available. Likewise, a defvar only announces the
presence of a variable, but the binding and its value will not be
available at compile time.
However, as almost always in Common Lisp ;), there are exceptions to
this rules: Some top-level forms do indeed have effects at compile time.
For example, a defmacro definition _will_ be fully available at compile
time. The reason is that you typically want to base subsequent code on
your macro definitions, and the compiler must be able to completely
macro-expand away these macro definitions as well. But this in turn
means that all the functions that a macro uses to produce the expansion
must also be available at compile time - but for function definitions,
this is typically not the case, as I explained above.
The (eval-when ...) form is there to exactly provide you with the a way
to tell the compiler that, say, a global function definition should be
available at compile time as well (or even to tell the compiler that it
is _only_ available at compile time, etc.).
However, using eval-when all over the place is ugly and can be hard to
deal with. A better way to organize your code is to take advantage of
systems. A system definition allows you to declare that one file of lisp
code depends on some other file of lisp code. By default, a system
definition will process a file by compiling _and_ loading it before
proceeding to the next file, so if a macro definition depends on the
presence of some functions at compile time, it is a good to put all the
support code in another file and declare in the system definition that
your macros depend on that support code. Then you can safely forget
about (eval-when ...) in 95% of the time.
And this also aligns well with another rule of thumb when implementing
macros (at least more complex ones): It is a good idea to provide a
functional layer of what you want to express at one level, i.e., purely
by use of defuns, and then to provide macros to ease using the
functional layer at a higher level. Just put the functional layer in one
file (or set of files), and the macro / syntactic layer in another file
(or set of files), and declare the correct dependencies in a system
definition - done.
I hope this helps.
3rd European Lisp Workshop
July 3 - Nantes, France - co-located with ECOOP 2006