Subject: Re: Macro mysteries ... please help ...
From: rpw3@rpw3.org (Rob Warnock)
Date: Thu, 07 May 2009 22:31:04 -0500
Newsgroups: comp.lang.lisp
Message-ID: <Y_ednR3CPK7lOp7XnZ2dnUVZ_q6dnZ2d@speakeasy.net>
Kaz Kylheku  <kkylheku@gmail.com> wrote:
+---------------
| Frank GOENNINGER <dg1sbg@googlemail.com> wrote:
| > I have the following code:
| > (defmacro eval-now! (&body body)
| >   `(eval-when (:compile-toplevel :load-toplevel :execute)
| >      ,@body))
| 
| Bad name; this should be called EVAL-ALWAYS (i.e. in all situations).
+---------------

Heh. Mine got called simply EVAL-WHEN* by analogy to shell wildcarding. ;-}

[...not to mention the general CL convention of adding a "*" suffix
to indicate local tweaks of standard forms.]

+---------------
| >   (defmacro define-constant (name value &optional docstring)
| >    "Define a constant properly.  If NAME is unbound, DEFCONSTANT
| 
| DEFCONSTANT defines a constant properly.
+---------------

And its "constant" status must be made available to the user
at compile time:

    Macro DEFCONSTANT
    ...
    If a defconstant form appears as a top level form, the
    compiler must recognize that name names a constant variable.
    ...

and:

    Function CONSTANTP
    ... symbols declared as constant by the user in the indicated
    environment using DEFCONSTANT are always considered constant forms
    and must be recognized as such by CONSTANTP.

*BUT*... The compiler does *not* necessarily bind a constant variable
to a *value* at compile time. Again from "Macro DEFCONSTANT":
   
    An implementation may choose to evaluate the value-form at
    compile time, load time, or both.

If the compiler chooses load-time only [e.g., as CMUCL does],
then the new constant variable will be "constant but unbound"
in subsequent macro expansions. For example:

    (defconstant +foo+ 1234)

    (defmacro dbgv (&rest forms)
      `(eval-when (:compile-toplevel :load-toplevel :execute)
	,@(loop for form in forms collect
	  `(format t "DBGV: ~s = ~s~%" ',form ,form))))

    (dbgv (constantp '+foo+)
	  (boundp '+foo+)
	  (when (boundp '+foo+) +foo+))  

If you COMPILE-FILE the above with CMUCL, you'll get:

    cmu> (compile-file "foo.lisp")

    ; Python version 1.1, VM version Intel x86 on 06 MAY 09 05:56:20 pm.
    ; Compiling: /u/rpw3/foo.lisp 06 MAY 09 05:56:10 pm

    DBGV: (CONSTANTP (QUOTE +FOO+)) = T
    DBGV: (BOUNDP (QUOTE +FOO+)) = NIL
    DBGV: (WHEN (BOUNDP (QUOTE +FOO+)) +FOO+) = NIL
    ; Byte Compiling Top-Level Form:

    ; foo.x86f written.
    ; Compilation finished in 0:00:00.
    #P"/u/rpw3/foo.x86f"
    NIL
    NIL
    cmu> 

This is perfectly legal, but means that CONSTANTP cannot portably or
reliably be used to tell whether a value is available at compile time,
even if it is known to be "constant". Therefore macros that depend upon
CONSTANTP for that information [instead of BOUNDP] are non-portable.

Note that "the right thing" happens at load time, of course:

    cmu> (load *)

    ; Loading #P"/u/rpw3/foo.x86f".
    DBGV: (CONSTANTP (QUOTE +FOO+)) = T
    DBGV: (BOUNDP (QUOTE +FOO+)) = T
    DBGV: (WHEN (BOUNDP (QUOTE +FOO+)) +FOO+) = 1234
    T
    cmu> 

Finally, another "gotcha" with DEFCONSTANT is implementations
which choose the "both" choice in the above text, repeated here:

    An implementation may choose to evaluate the value-form at
    compile time, load time, or both.

because of the very next sentence:

    Therefore, users must ensure that the initial-value can be evaluated
    at compile time (regardless of whether or not references to name
    appear in the file) and that it always evaluates to the same value.

Therefore macros that depend on DEFCONSTANT to evaluate the value-form
only at load-time are *also* non-portable. You have been warned!  ;-}


-Rob

p.s. The above mild rant is the result of some problems I had once
with a new version of HTOUT. Turned out that Tim Bradshaw and I were
using CL implementations that did different things with DEFCONSTANT
value-forms. [The current version of HTOUT has a fix for that.]

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607