From: Robert E. Brown
Subject: defconstant warnings
Date: 
Message-ID: <87ptgqmcdm.fsf@loki.bibliotech.com>
I'm experimenting with the well known technique of having a macro expand a
polynomial expresion at compile time using Horner's rule.

My difficulty is that loading the code below multiple times into SBCL
generates warnings about +coeffs+ being redefined.  The warnings seem
legitimate to me, since +coeffs+ really does get redefined when the file is
loaded more than once.

What is the the approach to getting rid of the warning message?  Should I
bind +coeffs+ with defparameter instead?  Should I wrap one or two of the
forms below with eval-when?

Note that I'm using +coeffs+ twice, once just to define +recip-sum+.  I
don't really need the value at run time, just at compile or load time.

Thanks for taking a look.

                        bob

====================

(defmacro poly-eval (x coeffs)
  "For COEFFS list (a0 a1 a2 ...) produce an expression that evaluates the
polynomial a0 + a1*x + a2*x^2 + ..."
  (if (endp (rest coeffs))
      (first coeffs)
      `(+ (* ,x (poly-eval ,x ,(rest coeffs))) ,(first coeffs))))

(defconstant +coeffs+
  '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
    1.330274429d0))

(defconstant +recip-sum+ (/ (apply #'+ +coeffs+)))

(defun foobar (x)
  (* +recip-sum+ (poly-eval x #.+coeffs+)))

From: Erann Gat
Subject: Re: defconstant warnings
Date: 
Message-ID: <myfirstname.mylastname-2110031630050001@k-137-79-50-101.jpl.nasa.gov>
In article <··············@loki.bibliotech.com>, ······@speakeasy.net
(Robert E. Brown) wrote:

> I'm experimenting with the well known technique of having a macro expand a
> polynomial expresion at compile time using Horner's rule.
> 
> My difficulty is that loading the code below multiple times into SBCL
> generates warnings about +coeffs+ being redefined.  The warnings seem
> legitimate to me, since +coeffs+ really does get redefined when the file is
> loaded more than once.
> 
> What is the the approach to getting rid of the warning message?  Should I
> bind +coeffs+ with defparameter instead?

If you're going to take this route you probably want defvar, not
defparameter.  DEFVAR, I have recently learned to my astonishment, defines
dynamic variables that are intended to remain constant (go figure) across
file reloads.

You might also consider making +coefs+ a symbol macro.

E.
From: Frode Vatvedt Fjeld
Subject: Re: defconstant warnings
Date: 
Message-ID: <2hr816jfh7.fsf@vserver.cs.uit.no>
······················@jpl.nasa.gov (Erann Gat) writes:

> [..] DEFVAR, I have recently learned to my astonishment, defines
> dynamic variables that are intended to remain constant (go figure)
> across file reloads.

I think this is very much the wrong way to look at it. Defvar is the
way it is precisely because the variable is intended to change. But
lisp is an interactive language, so a change is what happens when
someone says (setf *foo* 'bar) at the REPL. It's not when someone
edits a file and then reloads it. Rather, chances are usually good
that the file is being reloaded for some other reason than resetting a
variable to its init-form value, and that you don't want to undo the
explicit and hard-earned (setf *foo* bla-bla-bla) from some hundred
interactive commands back in time.


[Sorry for hi-jacking this thread when I didn't bother to follow the
on-topic one..]

-- 
Frode Vatvedt Fjeld
From: Erann Gat
Subject: Re: defconstant warnings
Date: 
Message-ID: <myfirstname.mylastname-2110031735170001@k-137-79-50-101.jpl.nasa.gov>
In article <··············@vserver.cs.uit.no>, Frode Vatvedt Fjeld
<······@cs.uit.no> wrote:

> ······················@jpl.nasa.gov (Erann Gat) writes:
> 
> > [..] DEFVAR, I have recently learned to my astonishment, defines
> > dynamic variables that are intended to remain constant (go figure)
> > across file reloads.
> 
> I think this is very much the wrong way to look at it. Defvar is the
> way it is precisely because the variable is intended to change. But
> lisp is an interactive language, so a change is what happens when
> someone says (setf *foo* 'bar) at the REPL. It's not when someone
> edits a file and then reloads it. Rather, chances are usually good
> that the file is being reloaded for some other reason than resetting a
> variable to its init-form value, and that you don't want to undo the
> explicit and hard-earned (setf *foo* bla-bla-bla) from some hundred
> interactive commands back in time.

Ah.  That makes sense I suppose.  Didn't occur to me since I don't
generally keep intermediate results in global variables (being an OO fan)
but to each his own.  I use dynamic globals as parameters.  I guess that
should have been a clue.

E.
From: Nils Goesche
Subject: Re: defconstant warnings
Date: 
Message-ID: <87d6cq0zbv.fsf@darkstar.cartan>
······················@jpl.nasa.gov (Erann Gat) writes:

> In article <··············@vserver.cs.uit.no>, Frode Vatvedt Fjeld
> <······@cs.uit.no> wrote:
> 
> > ······················@jpl.nasa.gov (Erann Gat) writes:
> > 
> > > [..] DEFVAR, I have recently learned to my astonishment, defines
> > > dynamic variables that are intended to remain constant (go figure)
> > > across file reloads.
> > 
> > I think this is very much the wrong way to look at it. Defvar is the
> > way it is precisely because the variable is intended to change. But
> > lisp is an interactive language, so a change is what happens when
> > someone says (setf *foo* 'bar) at the REPL. It's not when someone
> > edits a file and then reloads it. Rather, chances are usually good
> > that the file is being reloaded for some other reason than resetting a
> > variable to its init-form value, and that you don't want to undo the
> > explicit and hard-earned (setf *foo* bla-bla-bla) from some hundred
> > interactive commands back in time.
> 
> Ah.  That makes sense I suppose.  Didn't occur to me since I don't
> generally keep intermediate results in global variables (being an OO fan)
> but to each his own.  I use dynamic globals as parameters.  I guess that
> should have been a clue.

I typically use DEFVAR when I put some complicated object there
the computation of which takes a long while, or if it represents
a network connection that might be still active while I'm still
editing and debugging sources or something: I can easily
recompile and load the whole file without losing the network
connection.

Regards,
-- 
Nils G�sche
"Don't ask for whom the <CTRL-G> tolls."

PGP key ID #xD26EF2A0
From: Alex Mizrahi
Subject: Re: defconstant warnings
Date: 
Message-ID: <bn6m10$tv0c8$1@ID-177567.news.uni-berlin.de>
(message (Hello 'Nils)
(you :wrote  :on '(22 Oct 2003 04:40:36 +0200))
(

 NG> I typically use DEFVAR when I put some complicated object there the
 NG> computation of which takes a long while, or if it represents a
 NG> network connection that might be still active while I'm still
 NG> editing and debugging sources or something: I can easily recompile
 NG> and load the whole file without losing the network connection.

by the way, what do people think about passing some stuff via special
variables that are bound in functions?
like *request in:

(defvar *request* nil)

(defun process-request ()
    (let ((*request* (read-request)))
        (handler)))

(defun handler ()
    (do-something-with *request*))

i've seen such approach in mod_lisp example for cmucl - they do pass socket
to apache (that is owned by thread) in such way.
from my experience with other languages, global variables are bad, so i
passed everything that is needed via function parameters. but it appears
that with such global variables refactoring is much easier, as well as code
is typically shorter/cleaner since i don't have to pass everything via
params, so i rewrote it to use global variables (2 so far - *request* and
*responce-headers*).

so, is it a good approach?

)
(With-best-regards '(Alex Mizrahi) :aka 'killer_storm)
(prin1 "Jane dates only Lisp programmers"))
From: Paul Dietz
Subject: Re: defconstant warnings
Date: 
Message-ID: <3F96EB68.BDD10919@motorola.com>
Alex Mizrahi wrote:

> by the way, what do people think about passing some stuff via special
> variables that are bound in functions?
> like *request in:
> 
> (defvar *request* nil)
> 
> (defun process-request ()
>     (let ((*request* (read-request)))
>         (handler)))
> 
> (defun handler ()
>     (do-something-with *request*))


When I do this, instead of defvar I like to just do

(declaim (special *request*))

so it can detect if I forgot to bind it.

	Paul
From: Peter Seibel
Subject: Re: defconstant warnings
Date: 
Message-ID: <m3n0bt2d0l.fsf@javamonkey.com>
Paul Dietz <············@motorola.com> writes:

> Alex Mizrahi wrote:
> 
> > by the way, what do people think about passing some stuff via special
> > variables that are bound in functions?
> > like *request in:
> > 
> > (defvar *request* nil)
> > 
> > (defun process-request ()
> >     (let ((*request* (read-request)))
> >         (handler)))
> > 
> > (defun handler ()
> >     (do-something-with *request*))
> 
> 
> When I do this, instead of defvar I like to just do
> 
> (declaim (special *request*))
> 
> so it can detect if I forgot to bind it.

How is this different in effect than just:

  (defvar *request*)

?

FWIW, in SBCL:

  * (macroexpand-1 '(defvar *request*))

  (PROGN (DECLAIM (SPECIAL *REQUEST*)) '*REQUEST*)
  T
  * 

and CLISP:

  [1]> (macroexpand-1 '(defvar *request*))
  (LET NIL (PROCLAIM '(SPECIAL *REQUEST*)) '*REQUEST*) ;
  T

and Allegro:

  CL-USER(114): (macroexpand-1 '(defvar *request*))
  (PROGN (DECLAIM (SPECIAL *REQUEST*)) (RECORD-SOURCE-FILE '*REQUEST* :TYPE :SPECIAL-DECLARATION) '*REQUEST*)
  T

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Barry Margolin
Subject: Re: defconstant warnings
Date: 
Message-ID: <52Blb.166$lK3.89@news.level3.com>
In article <··············@ID-177567.news.uni-berlin.de>,
Alex Mizrahi <·········@xhotmail.com> wrote:
>by the way, what do people think about passing some stuff via special
>variables that are bound in functions?
>like *request in:
>
>(defvar *request* nil)
>
>(defun process-request ()
>    (let ((*request* (read-request)))
>        (handler)))
>
>(defun handler ()
>    (do-something-with *request*))
>
>i've seen such approach in mod_lisp example for cmucl - they do pass socket
>to apache (that is owned by thread) in such way.
>from my experience with other languages, global variables are bad, so i
>passed everything that is needed via function parameters. but it appears
>that with such global variables refactoring is much easier, as well as code
>is typically shorter/cleaner since i don't have to pass everything via
>params, so i rewrote it to use global variables (2 so far - *request* and
>*responce-headers*).
>
>so, is it a good approach?

It works in cases where you can be *sure* that a dynamic call chain only
has to deal with one of these objects.  As in mod_lisp, where it's always
processing just one HTTP request, so the attributes of that request can be
in a dynamic variable binding.  It's not really elegant programming style,
but it's often practical.  It avoids having to pass lots of parameters all
over the place, just because some of them will be needed further down in
the call chain.

However, a good solution to that is to group everything into a structure or
class.  Then you just have to pass one object around, and each function can
access the slots that it cares about out of that structure.

-- 
Barry Margolin, ··············@level3.com
Level(3), Woburn, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: james anderson
Subject: Re: defconstant warnings
Date: 
Message-ID: <3F95C115.59A253DA@setf.de>
given that you are not only treating the variable as a constant, but
specifying that it be bound at read-time, and that there appears to be a 1-1
relation between function and coefficient set, what argues against expressing that?

"Robert E. Brown" wrote:
> 
> I'm experimenting with the well known technique of having a macro expand a
> polynomial expresion at compile time using Horner's rule.
> 
>...
> ====================
> 
> (defmacro poly-eval (x coeffs)
>   "For COEFFS list (a0 a1 a2 ...) produce an expression that evaluates the
> polynomial a0 + a1*x + a2*x^2 + ..."
>   (if (endp (rest coeffs))
>       (first coeffs)
>       `(+ (* ,x (poly-eval ,x ,(rest coeffs))) ,(first coeffs))))
> 
> (defconstant +coeffs+
>   '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
>     1.330274429d0))
> 
> (defconstant +recip-sum+ (/ (apply #'+ +coeffs+)))
> 
> (defun foobar (x)
>   (* +recip-sum+ (poly-eval x #.+coeffs+)))


(defMacro defPoly (name &rest coefficients)
  (labels ((poly-eval (x coeffs)
           (if (endp (rest coeffs))
             (first coeffs)
             `(+ (* ,x ,(poly-eval x (rest coeffs))) ,(first coeffs)))))
    `(defun ,name (x)
       (* ,(/ (apply #'+ coefficients))
          ,(poly-eval 'x coefficients)))))

(defPoly foobar 0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
    1.330274429d0)
From: Adam Warner
Subject: Re: defconstant warnings
Date: 
Message-ID: <pan.2003.10.22.01.39.40.712690@consulting.net.nz>
Hi Robert E. Brown,

> (defconstant +coeffs+
>   '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
>     1.330274429d0))
> 
> (defconstant +recip-sum+ (/ (apply #'+ +coeffs+)))

SBCL attempts to be strict to ANSI Common Lisp issues to the point where
you may find it somewhat frustrating. The lists when read again are not
EQL. "The consequences are undefined if there are any bindings of the
variable named by name at the time defconstant is executed or if the value
is not eql to the value of initial-value." So when you load the file
again, '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
1.330274429d0) is not EQL to '(0.0d0 0.31938153d0 -0.356563782d0
1.781477937d0 -1.821255978d0 1.330274429d0), and SBCL interrupts and makes
you choose whether to go ahead and change the value of +coeffs+ to the
same value.

The good news for you is that I recently created a macro to overcome these
issues:

(defmacro defconstant-skip (test name initial-value &optional documentation)
  "Skip constant redefinition if a #'test of the original and new value is
true. If the #'test is false the constant is redefined, with a warning."
  `(handler-bind
    ((#+sbcl defconstant-uneql #+(not sbcl) simple-error
      (lambda (condition)
        (declare (ignorable condition) (optimize inhibit-warnings))
        (if (funcall ,test ,name ,initial-value)
            (abort)
            (progn
              (warn (format nil
                            "Redefining constant ~A from ~S to ~S: Not ~A."
                            ',name ,name ,initial-value ',test))
              (continue))))))
    (defconstant ,name ,initial-value ,documentation)))

Replace:

(defconstant +coeffs+
  '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
    1.330274429d0))

With:

(defconstant-skip #'equal +coeffs+
  '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
    1.330274429d0))

And so on and you will never again have these issues. The constant
redefinition is skipped if the "new" value for the constant is equal to
the test. EQUAL is the appropriate test for this list.

This is needed in .emacs to get appropriate indentation and highlighting:
;;clobber conventional elisp indentation.
(setf lisp-indent-function #'common-lisp-indent-function)

(put 'defconstant-skip 'common-lisp-indent-function 2)
(font-lock-add-keywords 'lisp-mode
  (list (cons (concat "(" (regexp-opt '("defconstant-skip") t) "\\>") 'font-lock-keyword-face)))

The code contained in this message is dedicated in perpetuity to
the public domain. If for any reason residual ownership to the code
remains it is permissively licensed under the terms of "The MIT License"
as set out at <http://www.opensource.org/licenses/mit-license.php>.

Regards,
Adam
From: Christophe Rhodes
Subject: Re: defconstant warnings
Date: 
Message-ID: <sqznftis18.fsf@lambda.jcn.srcf.net>
Adam Warner <······@consulting.net.nz> writes:

> Hi Robert E. Brown,
>
>> (defconstant +coeffs+
>>   '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
>>     1.330274429d0))
>> 
>> (defconstant +recip-sum+ (/ (apply #'+ +coeffs+)))
>
> SBCL attempts to be strict to ANSI Common Lisp issues to the point where
> you may find it somewhat frustrating. The lists when read again are not
> EQL. "The consequences are undefined if there are any bindings of the
> variable named by name at the time defconstant is executed or if the value
> is not eql to the value of initial-value." 

Just to introduce a little rationale: this (perhaps somewhat perverse)
error signalling is (was?  Sheesh, sbcl is nearly five years old! :-)
important in sbcl's own development -- since the intention is that
sbcl is written in conforming ANSI Common Lisp, but development of
necessity in the early years was done using it or close cousins as
host Lisps, defensive programming meant detecting this kind of
undefined behaviour and telling the sbcl developer about it.

> (defmacro defconstant-skip (test name initial-value &optional documentation)
>   "Skip constant redefinition if a #'test of the original and new value is
> true. If the #'test is false the constant is redefined, with a warning."
>   `(handler-bind
>     ((#+sbcl defconstant-uneql #+(not sbcl) simple-error
>       (lambda (condition)
>         (declare (ignorable condition) (optimize inhibit-warnings))
>         (if (funcall ,test ,name ,initial-value)
>             (abort)
>             (progn
>               (warn (format nil
>                             "Redefining constant ~A from ~S to ~S: Not ~A."
>                             ',name ,name ,initial-value ',test))
>               (continue))))))
>     (defconstant ,name ,initial-value ,documentation)))

This macro doesn't do what you think it does.  In particular, it
destroys the compile-time effect of DEFCONSTANT, because in your
expansion the DEFCONSTANT is no longer at toplevel, and so isn't
processed specially by the file compiler.

The intention behind the DEFCONSTANT-UNEQL condition that I introduced
was so that quasi-portable legacy codebases (e.g. CLX), written before
this fine point was considered, could compile without modification if
the driver wrapped the call to COMPILE-FILE with such a handler as
above; for instance, from clx.asd

#+sbcl
(defmethod perform :around (o (f clx-source-file))
  ;; SBCL signals an error if DEFCONSTANT is asked to redefine a
  ;; constant unEQLly.  For CLX's purposes, however, we are defining
  ;; structured constants (lists and arrays) not for EQLity, but for
  ;; the purposes of constant-folding operations such as (MEMBER FOO
  ;; +BAR+), so it is safe to abort the redefinition provided the
  ;; structured data is sufficiently equal.
  (handler-bind
      ((sb-ext:defconstant-uneql
         (lambda (c)
           ;; KLUDGE: this really means "don't warn me about
           ;; efficiency of generic array access, please"
           (declare (optimize (sb-ext:inhibit-warnings 3)))
           (let ((old (sb-ext:defconstant-uneql-old-value c))
                 (new (sb-ext:defconstant-uneql-new-value c)))
             (typecase old
               (list (when (equal old new) (abort c)))
               (string (when (and (typep new 'string)
                                  (string= old new))
                         (abort c)))
               ...)))))
    (call-next-method)))

You can vaguely restore correctness in your macro by wrapping the
expansion in EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE),
but that's still not quite right; sbcl itself does

(defmacro defconstant-eqx (symbol expr eqx &optional doc)
  `(def!constant ,symbol
     (%defconstant-eqx-value ',symbol ,expr ,eqx)
     ,@(when doc (list doc))))
(defun %defconstant-eqx-value (symbol expr eqx)
  (declare (type function eqx))
  (flet ((bummer (explanation)
           (error ··@<bad DEFCONSTANT-EQX ~S ~2I~_~S: ~2I~_~A ~S~:>"
                  symbol
                  expr
                  explanation
                  (symbol-value symbol))))
    (cond ((not (boundp symbol))
           expr)
          ((not (constantp symbol))
           (bummer "already bound as a non-constant"))
          ((not (funcall eqx (symbol-value symbol) expr))
           (bummer "already bound as a different constant value"))
          (t
           (symbol-value symbol)))))

but this isn't entirely portable, I think, because DEFCONSTANT at
compile-time need not establish the symbol-value, but merely make the
symbol -> value association known to the compiler; however, since EVAL
doesn't take an environment argument, I think this is the best we can
do :-/

Cheers,

Christophe
-- 
http://www-jcsu.jesus.cam.ac.uk/~csr21/       +44 1223 510 299/+44 7729 383 757
(set-pprint-dispatch 'number (lambda (s o) (declare (special b)) (format s b)))
(defvar b "~&Just another Lisp hacker~%")    (pprint #36rJesusCollegeCambridge)
From: Adam Warner
Subject: Re: defconstant warnings
Date: 
Message-ID: <pan.2003.10.22.10.31.56.52708@consulting.net.nz>
Hi Christophe Rhodes,

> Adam Warner <······@consulting.net.nz> writes:
> 
>> Hi Robert E. Brown,
>>
>>> (defconstant +coeffs+
>>>   '(0.0d0 0.31938153d0 -0.356563782d0 1.781477937d0 -1.821255978d0
>>>     1.330274429d0))
>>> 
>>> (defconstant +recip-sum+ (/ (apply #'+ +coeffs+)))
>>
>> SBCL attempts to be strict to ANSI Common Lisp issues to the point where
>> you may find it somewhat frustrating. The lists when read again are not
>> EQL. "The consequences are undefined if there are any bindings of the
>> variable named by name at the time defconstant is executed or if the value
>> is not eql to the value of initial-value." 
> 
> Just to introduce a little rationale: this (perhaps somewhat perverse)
> error signalling is (was?  Sheesh, sbcl is nearly five years old! :-)
> important in sbcl's own development -- since the intention is that
> sbcl is written in conforming ANSI Common Lisp, but development of
> necessity in the early years was done using it or close cousins as
> host Lisps, defensive programming meant detecting this kind of
> undefined behaviour and telling the sbcl developer about it.
> 
>> (defmacro defconstant-skip (test name initial-value &optional documentation)
>>   "Skip constant redefinition if a #'test of the original and new value is
>> true. If the #'test is false the constant is redefined, with a warning."
>>   `(handler-bind
>>     ((#+sbcl defconstant-uneql #+(not sbcl) simple-error
>>       (lambda (condition)
>>         (declare (ignorable condition) (optimize inhibit-warnings))
>>         (if (funcall ,test ,name ,initial-value)
>>             (abort)
>>             (progn
>>               (warn (format nil
>>                             "Redefining constant ~A from ~S to ~S: Not ~A."
>>                             ',name ,name ,initial-value ',test))
>>               (continue))))))
>>     (defconstant ,name ,initial-value ,documentation)))
> 
> This macro doesn't do what you think it does.  In particular, it
> destroys the compile-time effect of DEFCONSTANT, because in your
> expansion the DEFCONSTANT is no longer at toplevel, and so isn't
> processed specially by the file compiler.

Many thanks for the above and snipped explanations.

I am aware of destroying the compile-time effect of DEFCONSTANT. I
definitely should have mentioned that I wrap the DEFCONSTANT-SKIP
definitions in (eval-when (:compile-toplevel :load-toplevel :execute)
...). In particular the macro does do what I think it does. I simply
neglected to impart a crucial piece of information.

Thanks for the discussion of other potential portability issues. I am yet
to run across them (other than having to know the error that is signalled).

Regards,
Adam
From: Adam Warner
Subject: Re: defconstant warnings
Date: 
Message-ID: <pan.2003.10.23.00.21.57.665181@consulting.net.nz>
Hi Christophe Rhodes,

> You can vaguely restore correctness in your macro by wrapping the
> expansion in EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE)
> ...

Just a note for those who didn't realise it, EVAL-WHEN works perfectly
when generated by the macro itself, e.g.:

(defmacro defconstant-skip (test name initial-value &optional documentation)
  "Skip constant redefinition if a #'test of the original and new value is
true. If the #'test is false the constant is redefined, with a warning."
  `(eval-when (:compile-toplevel :load-toplevel :execute)
    (handler-bind
      ((#+sbcl defconstant-uneql #+(not sbcl) simple-error
        (lambda (condition)
          (declare (ignorable condition) (optimize inhibit-warnings))
          (if (funcall ,test ,name ,initial-value)
              (abort)
              (progn
                (warn (format nil
                              "Redefining constant ~A from ~S to ~S: Not ~A."
                              ',name ,name ,initial-value ',test))
                (continue))))))
      (defconstant ,name ,initial-value ,documentation))))

The HyperSpec even contains an example on this point:

   * Macros intended for use in top level forms should be written so that
     side-effects are done by the forms in the macro expansion. The
     macro-expander itself should not do the side-effects.

     For example:

     Wrong:

     (defmacro foo ()
       (really-foo)
       `(really-foo))

     Right:

     (defmacro foo ()
       `(eval-when (:compile-toplevel :execute :load-toplevel) (really-foo)))

     Adherence to this convention means that such macros behave
     intuitively when appearing as non-top-level forms.

I was about to attempt "Wrong" before reasoning that after macroexpansion
the EVAL-WHEN must be a top level form at compile time if the macro name
appears as a top-level form. Therefore it should have the same compile
time effect as if it was typed in explicitly.

It's nice when Kent has read your mind.

Regards,
Adam