From: Artem Baguinski
Subject: (eval-when?)
Date: 
Message-ID: <42ef8397$0$753$5fc3050@dreader2.news.tiscali.nl>
Dear Lispniks

I am a bit confused about compile/load/execute time evaluation.

Say, I have a package and i have some constants defined there with 
(defconstant ...)

If now I issue M-x slime-compile-and-load-file command I get warnings 
about the constants being redefined (and even am thrown in a debugger).

I gather it's because when I compiled the file the defconstant forms 
where already evealuated and when the lisp loads the file it has to 
evaluate them again. but I don't understand why it happens like that. I 
thought compiling file just creates the compiled version of my .lisp file?

on the other hand, i also used a loop to generate some of the constants 
and give them values, something like:

   (do ((n 0 (+ 1 n))
        (note-list '((B# C) (C# Db) (D)
		    (D# Eb) (E Fb) (E# F)
		    (F# Gb) (G) (G# Ab)
		    (A) (A# Bb) (B Cb)) (rest note-list)))
       ((null note-list) nil)
     (mapc #'(lambda (alias)
	      (eval `(defconstant ,alias ,n)))
	  (car note-list)))

and other code depends on the generated constants:

   (defconstant +standard-tuning+
     (list E B G D A E)

Now I can't compile the file because on compile time the constants E B G 
... aren't generated yet. I feel like I have to use eval-when form but i 
don't really understand the rules. Also my constant generation uses eval 
form and I keep hearing that it's something to avoid.

Where can I read an explanation about these different "situation" 
(compile time, load time, execute) and how to use eval-when? Something 
more newbie friendly then HyperSpec (which works for me in most cases, 
but not always).

Or feel free to say: all that you're doing is wrong and you have to do 
it like this (or like that).

From: Peter Seibel
Subject: Re: (eval-when?)
Date: 
Message-ID: <m2oe8g4cok.fsf@gigamonkeys.com>
Artem Baguinski <····@v2.nl> writes:

> Dear Lispniks
>
> I am a bit confused about compile/load/execute time evaluation.
>
> Say, I have a package and i have some constants defined there with
> (defconstant ...)
>
> If now I issue M-x slime-compile-and-load-file command I get warnings
> about the constants being redefined (and even am thrown in a
> debugger).
>
> I gather it's because when I compiled the file the defconstant forms
> where already evealuated and when the lisp loads the file it has to
> evaluate them again. but I don't understand why it happens like
> that. I thought compiling file just creates the compiled version of my
> .lisp file?

That's true. But slime-compile-and-load-file, not suprisingly also
loads the file.

> on the other hand, i also used a loop to generate some of the
> constants and give them values, something like:
>
>    (do ((n 0 (+ 1 n))
>         (note-list '((B# C) (C# Db) (D)
> 		    (D# Eb) (E Fb) (E# F)
> 		    (F# Gb) (G) (G# Ab)
> 		    (A) (A# Bb) (B Cb)) (rest note-list)))
>        ((null note-list) nil)
>      (mapc #'(lambda (alias)
> 	      (eval `(defconstant ,alias ,n)))
> 	  (car note-list)))
>
> and other code depends on the generated constants:
>
>    (defconstant +standard-tuning+
>      (list E B G D A E)

So this is asking for undefined behavior since if you evaluate this
form twice the (list ...) form will evaluate to two different,
i.e. non EQL, values. I'd probably use DEFPARAMETER for this.

> Now I can't compile the file because on compile time the constants E
> B G ... aren't generated yet. I feel like I have to use eval-when
> form but i don't really understand the rules. Also my constant
> generation uses eval form and I keep hearing that it's something to
> avoid.
>
> Where can I read an explanation about these different "situation"
> (compile time, load time, execute) and how to use eval-when? Something
> more newbie friendly then HyperSpec (which works for me in most cases,
> but not always).

I like to think the explanation in the section EVAL-WHEN in

  <http://www.gigamonkeys.com/book/the-special-operators.html>

is pretty understandable. But I don't think EVAL-WHEN is really the
solution to this particular problem.

> Or feel free to say: all that you're doing is wrong and you have to do
> it like this (or like that).

Okay. I'd do it something like this:

  (defmacro define-notes ((start) &body names)
    `(progn
       ,@(loop for names in names 
              for i from start nconc
              (loop for n in names collect `(defconstant ,n ,i)))))

  (define-notes (0)
    (B# C) (C# Db) (D) (D# Eb) (E Fb) (E# F) (F# Gb) (G) (G# Ab) (A) (A# Bb) (B Cb))

You may still get warnings about redefining constants if you eval the
DEFINE-NOTES form and then later compile and load the whole file. But
if you do you can ignore them since the values of your constants are
in fact the same (i.e. EQL). Certainly you should not end up in the
debugger. 

-Peter

-- 
Peter Seibel           * ·····@gigamonkeys.com
Gigamonkeys Consulting * http://www.gigamonkeys.com/
Practical Common Lisp  * http://www.gigamonkeys.com/book/
From: Artem Baguinski
Subject: Re: (eval-when?)
Date: 
Message-ID: <42ef8d13$0$714$5fc3050@dreader2.news.tiscali.nl>
Peter Seibel wrote:
> Artem Baguinski <····@v2.nl> writes:
> 
> 

Thanks Peter and Pascal

I thought I'd better go and read that chapter before I say something
stupid....

>> I gather it's because when I compiled the file the defconstant
>> forms where already evealuated and when the lisp loads the file it
>> has to evaluate them again. but I don't understand why it happens
>> like that. I thought compiling file just creates the compiled
>> version of my .lisp file?
> 
> 
> That's true. But slime-compile-and-load-file, not suprisingly also 
> loads the file.

...but no, I'm gonna do that in the inverse order.

- I understand that compile-and-load also loads
- now I also understand that I misused defconstant

but I still want to understand: why I get the error if I:

- start a fresh lisp interpreter
- open the source file
- issue compile-and-load ONCE

I guess to understand exactly why my code or my approach to working with
it is wrong I miss some understanding. Now I go and read that chapter.

P.S. Aha! I just noticed that I only get the warnings about the constant 
which I assigned lists to, so together with your (Peter and Pascal) 
comments it starts to make more sense...
From: Pascal Bourguignon
Subject: Re: (eval-when?)
Date: 
Message-ID: <87ek9c48il.fsf@thalassa.informatimago.com>
Artem Baguinski <····@v2.nl> writes:
> but I still want to understand: why I get the error if I:
>
> - start a fresh lisp interpreter
> - open the source file
> - issue compile-and-load ONCE

Because defconstant may have compile-time side-effects:

In sbcl:

* (with-open-file (src "example.lisp" :direction :output
                  :if-does-not-exist :create :if-exists :supersede)
   (print '(defconstant +example+ 42) src))


(DEFCONSTANT +EXAMPLE+ 42)
* (compile-file "example.lisp")


; compiling file "/local/users/pjb/example.lisp" (written 02 AUG 2005 06:11:48 PM):
; compiling (DEFCONSTANT +EXAMPLE+ ...)

; /local/users/pjb/example.fasl written
; compilation finished in 0:00:00
#P"/local/users/pjb/example.fasl"
NIL
NIL
* +example+

42
* 


In clisp:

[10]> (with-open-file (src "example.lisp" :direction :output
                  :if-does-not-exist :create :if-exists :supersede)
   (print '(defconstant +example+ 42) src))

(DEFCONSTANT +EXAMPLE+ 42)
[11]> (compile-file "example.lisp")

;; Compiling file /local/users/pjb/example.lisp ...
;; Wrote file /local/users/pjb/example.fas
0 errors, 0 warnings
#P"/local/users/pjb/example.fas" ;
NIL ;
NIL
[12]> +example+

*** - EVAL: variable +EXAMPLE+ has no value
The following restarts are available:
USE-VALUE      :R1      You may input a value to be used instead of +EXAMPLE+.
STORE-VALUE    :R2      You may input a new value for +EXAMPLE+.
ABORT          :R3      ABORT
Break 1 [13]> :a
[14]> 


> I guess to understand exactly why my code or my approach to working with
> it is wrong I miss some understanding. Now I go and read that chapter.

Now, as CLHS puts it:

   An implementation may choose to evaluate the value-form at compile
   time, load time, or both. 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.

if the value-form always evaluates to the same value, then it doesn't
matter if you run defconstant several times.  But if the value changes
(as per EQL), then you've got a problem:


In sbcl:

* (defconstant +example+ 42)

+EXAMPLE+
* (defconstant +example+ 42)

+EXAMPLE+
* (defconstant +example+ 42)

+EXAMPLE+
* (defconstant +example+ 43)

debugger invoked on a SB-EXT:DEFCONSTANT-UNEQL in thread 19391: The constant +EXAMPLE+ is being redefined (from 42 to 43)
See also:
  The ANSI Standard, Macro DEFCONSTANT
  The SBCL Manual, Node "Idiosyncrasies"

Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.

restarts (invokable by number or by possibly-abbreviated name):
  0: [CONTINUE] Go ahead and change the value.
  1: [ABORT   ] Keep the old value.
  2:            Exit debugger, returning to top level.

(SB-C::%DEFCONSTANT +EXAMPLE+ 43 NIL)
0]

In clisp:

[14]> (defconstant +example+ 42)
+EXAMPLE+
[15]> (defconstant +example+ 42)
+EXAMPLE+
[16]> (defconstant +example+ 42)
+EXAMPLE+
[17]> (defconstant +example+ 43)
WARNING: (DEFCONSTANT +EXAMPLE+ 43) redefines the constant +EXAMPLE+. Its old
         value was 42.
+EXAMPLE+
[18]> 


That's why you should use only value-form that are EQL-invariant:
numbers, characters or symbols.  Anything else should be named with a
normal variable instead of a constant variable:

(defconstant   +scalar+   42)
(defparameter  +sequence+ '(1 2 3))

I keep the convention that symbols enclosed in + are "constant".

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

This is a signature virus.  Add me to your signature and help me to live
From: Artem Baguinski
Subject: Re: (eval-when?)
Date: 
Message-ID: <42efacc7$0$736$5fc3050@dreader2.news.tiscali.nl>
Pascal Bourguignon wrote:
> Artem Baguinski <····@v2.nl> writes:
> 
>> but I still want to understand: why I get the error if I:
>> 
>> - start a fresh lisp interpreter - open the source file - issue
>> compile-and-load ONCE
> 
> 
> Because defconstant may have compile-time side-effects:
[...]
> 

I see. Really!

;-)

> (defconstant   +scalar+   42) 
 > (defparameter  +sequence+ '(1 2 3))
> 
> I keep the convention that symbols enclosed in + are "constant".

Thanks for explanation, it progressively makes more and more sense

> -- 
> __Pascal Bourguignon__             http://www.informatimago.com/
> 
> This is a signature virus. Add me to your signature and help me to 
> live

It mutated! the last time I encountered it, it said "help me spread" ;-)
From: Pascal Costanza
Subject: Re: (eval-when?)
Date: 
Message-ID: <3l9f9lF11ces0U1@individual.net>
Artem Baguinski wrote:
> Dear Lispniks
> 
> I am a bit confused about compile/load/execute time evaluation.
> 
> Say, I have a package and i have some constants defined there with 
> (defconstant ...)
> 
> If now I issue M-x slime-compile-and-load-file command I get warnings 
> about the constants being redefined (and even am thrown in a debugger).
> 
> I gather it's because when I compiled the file the defconstant forms 
> where already evealuated and when the lisp loads the file it has to 
> evaluate them again. but I don't understand why it happens like that. I 
> thought compiling file just creates the compiled version of my .lisp file?

This is not really an answer to your question, but: I wouldn't worry too 
much about defconstant. It's good for defining constant symbols, numbers 
and characters (everything that preserves identity under eql). In your 
case, you try to assign a freshly created list to a constant, and this 
is by definition not eql.

Recall why you want to use a constant: It's mainly for efficiency 
reasons. Since you cannot predict efficiency of large programs (and 
since accessing the elements of a list probably contribute more to the 
possible runtime overhead) you shouldn't spend your time on fixing 
defconstant issues.

Use defparameter or defvar instead. Just my 0.02�.


Pascal

-- 
In computer science, we stand on each other's feet. - Brian K. Reid