Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Message from discussion apparently undefined function called by macro

Path: g2news2.google.com!news4.google.com!news.glorb.com!fu-berlin.de!uni-berlin.de!individual.net!not-for-mail
From: Pascal Costanza <p...@p-cos.net>
Newsgroups: comp.lang.lisp
Subject: Re: apparently undefined function called by macro
Date: Tue, 27 Jun 2006 23:22:32 +0200
Lines: 106
Message-ID: <4gdlsnF1n65dqU1@individual.net>
References: <12a2jt2ta2gk539@corp.supernews.com>
Mime-Version: 1.0
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
X-Trace: individual.net sU2YB2rYr+Qo96YYGaFrkArqnlZnjN1UX+Ddp+lBEpk+p4vrcH
User-Agent: Thunderbird 1.5.0.4 (Macintosh/20060530)
In-Reply-To: <12a2jt2ta2gk539@corp.supernews.com>

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:
> 
>     [1]> (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
> model.

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.


Pascal

-- 
3rd European Lisp Workshop
July 3 - Nantes, France - co-located with ECOOP 2006
http://lisp-ecoop06.bknr.net/