From: Dr. Edmund Weitz
Subject: EVAL-WHEN in REPL
Date: 
Message-ID: <m3elm379fj.fsf@bird.agharta.de>
This is what I get in LispWorks (and Allegro, CLISP, and ECLS),

  CL-USER 7 > (defparameter *foo* 3)
  *FOO*
  
  CL-USER 8 > (eval-when (:compile-toplevel) (setq *foo* 4))
  
  NIL
  
  CL-USER 9 > *foo*
  3
  
and it is pretty much what I expected. However, CMUCL (and SBCL)
behave differently:

  * (defparameter *foo* 3)
  *FOO*
  * (eval-when (:compile-toplevel) (setq *foo* 4))
  
  NIL
  * *foo*
  4

Now, I thought I was finally coming close to understanding EVAL-WHEN,
but I might be confused again.

It is my understanding that (SETQ *FOO* 4) above is a top level form
which is processed by EVAL because I typed it into the REPL. As I'm
not using COMPILE-FILE or LOAD, the usage op the situations
:COMPILE-TOPLEVEL or :LOAD-TOPLEVEL should have no effect on the
behaviour of EVAL-WHEN in this particular example.

The CLHS says that the SETQ form above must be processed when the
situation :EXECUTE is used. It doesn't say explicitely that it must
_not_ be processed when :EXECUTE is _not_ used, though. Therefore it
seems to be OK for LispWorks etc. not to process it. The CLHS also
says that EVAL-WHEN must return NIL in this case - which it does in
all implementations I've tested. So, they all seem to behave correctly
as far as I can tell. It seems to be implementation-dependent whether
the SETQ form will be processed for its side-effects or not.

What bothers me is the fact that - if my understanding is right -
there is obviously no way to have EVAL-WHEN control whether a form
will be processed or not in the REPL. Wouldn't it be more consistent
if the absence of the :EXECUTE situation would disallow the
implementation to process the form?

Thanks,
Edi.

From: Kent M Pitman
Subject: Re: EVAL-WHEN in REPL
Date: 
Message-ID: <sfwlmgbkwc0.fsf@shell01.TheWorld.com>
···@agharta.de (Dr. Edmund Weitz) writes:

> This is what I get in LispWorks (and Allegro, CLISP, and ECLS),
> 
>   CL-USER 7 > (defparameter *foo* 3)
>   *FOO*
>   
>   CL-USER 8 > (eval-when (:compile-toplevel) (setq *foo* 4))
>   
>   NIL
>   
>   CL-USER 9 > *foo*
>   3
>   
> and it is pretty much what I expected. However, CMUCL (and SBCL)
> behave differently:
> 
>   * (defparameter *foo* 3)
>   *FOO*
>   * (eval-when (:compile-toplevel) (setq *foo* 4))
>   
>   NIL
>   * *foo*
>   4
> 
> Now, I thought I was finally coming close to understanding EVAL-WHEN,
> but I might be confused again.
> 
> It is my understanding that (SETQ *FOO* 4) above is a top level form
> which is processed by EVAL because I typed it into the REPL. As I'm
> not using COMPILE-FILE or LOAD, the usage op the situations
> :COMPILE-TOPLEVEL or :LOAD-TOPLEVEL should have no effect on the
> behaviour of EVAL-WHEN in this particular example.

I tend to agree with this.  I'd be interested to hear why they though it
wasn't simply a bug.  Sometimes two people, especially two implementors,
since they've studied the spec closely but from different points of view,
will disagree on such matters.  But even so, it's worth asking...

> The CLHS says that the SETQ form above must be processed when the
> situation :EXECUTE is used. It doesn't say explicitely that it must
> _not_ be processed when :EXECUTE is _not_ used, though.

Well, that's not going to be what the issue is if there is one.

I often explain that the purpose of the compile-toplevel and the load-toplevel
is to do the "loop invariant" trick  (where execution time is a loop, that is,
it is done many times while the compilation was done only once).  At toplevel,
you have the ability to separate some of the work and have it done only once
at compile time to avoid its being done many times at runtime.  It makes no
(or, at least, little) sense to split the work for EVAL, as is appening to
you.  That's why we have three situations.  But I often say that
 :compile-toplevel + :load-toplevel = :execute
That is, you either do the first two or you do the second.  The first two
don't usually happen in the same image, either, though they could if you
immediately call LOAD after COMPILE-FILE.  But the first two are called
the compile-time-too situation, where you do work in compile time and then more
work in load-time.  As an equality, this isn't quite right because nearly 
always the :compile-toplevel + :load-toplevel does more than the :execute does.
But it's assumed the :compile-toplevel is invariant and that it's worth it
being a large constant factor because really it's
  1*<work-of-compiling> + n*<work-of-loading> = n*<work-of-executing>
and in that case you get a smaller value for the pair than for the single
action.  Anyway, this equation of mine fairly well tells you that the work
of each side of the equal sign is logically redundant in a given environment,
and that you should generally not be seing execute occur if load-toplevel
is going to occur, nor vice versa.  And sicne I think implicitly you've 
written:
 (progn (eval-when (:execute) ) ;do nothing
        (eval-when (:load-toplevel) ) ;do nothing
        (eval-when (:compile-toplevel) (setq *foo* 4)))
I think the setq has no business happening because I *do* think the :execute
has a business happening, and because I think both shouldn't happen together.
Consider:
 (progn (eval-when (:execute)
          (incf *prepared-foo-count*)
          (incf *deployed-foo-count*))
        (eval-when (:load-toplevel)
          (incf *deployed-foo-count*))
        (eval-when (:compile-toplevel)
          (incf *prepared-foo-count*)))
This program is not very robust against things like reloads, but ignoring
that, it's not expecting that you're going to do more than the applicable
arm, and in the evaluator, the applicable arm has to be the :execute, so I
claim by exclusion shouldn't be the others.

> Therefore it
> seems to be OK for LispWorks etc. not to process it.

No, LispWorks doesn't process it for a different reason, which I hope you
now see.

> The CLHS also
> says that EVAL-WHEN must return NIL in this case - which it does in
> all implementations I've tested. So, they all seem to behave correctly
> as far as I can tell. It seems to be implementation-dependent whether
> the SETQ form will be processed for its side-effects or not.

The reason it returns NIL is because otherwise you'd sometimes (i.e,, when
processing in one or another mode, like with EVAL vs with COMPILE-FILE,
which in some implementations shows you results of executions it does)
see values returned and otherwise not and might confuse yourself.  EVAL-WHEN
is *only* intended for side-effect.  Any value you want to yield can happen
only in the :execute case, and so you can always arrange for such a form not
to be EVAL-WHEN'd.  (If you don't see why this is true, give me a form you're
puzzled about and I'll show you the rewrite.)

> What bothers me is the fact that - if my understanding is right -
> there is obviously no way to have EVAL-WHEN control whether a form
> will be processed or not in the REPL.

No, your understanding is probably slightly off, but so is probably the
implementation you're working with.  So my guess is that you're making 
it worse  by trying to rationalize things prematurely.  I'll be curious to
hear what the CMU CL people say.

> Wouldn't it be more consistent
> if the absence of the :EXECUTE situation would disallow the
> implementation to process the form?

In this specific situation, yes, I think that's what is supposed to happen.

There was a great deal of confusion about the processing of EVAL-WHEN in 
pre-ANSI days.  We tried to clarify it better in ANSI CL, and even slightly
changed the definition (to accomodate it occurring in non-toplevel positions,
which it couldn't in CLTL1).  It's possible you're seeing holdover from 
earlier (mis)understandings.

A common misunderstanding of EVAL-WHEN by some implementors of CLTL1 was 
that (eval-when (:compile-toplevel) ...), which used to be confusingly called 
(eval-when (compile) ...), was supposed to execute if you were in a compiler.
So anyone who was doing 
 (defun eval (x)
   (funcall (compile nil `(lambda () ,x))))
thought they should compile such things because they were a compiler.
This was wrong.  It was me who suggested the annoyingly long and 
controversially hyphenated new keywords.  However much pain they are to 
spell and to fit into a small space, though, they do have the virtue of 
clarifying that (eval-when (eval) ...) has nothing to do with the use of
EVAL, nor does (eval-when (compile) ...) have to do with COMPILE.  At 
minimum, the extra morpheme "-TOPLEVEL" is supposed to invite you to ask
questions people never used to ask, which you apparently have, so I guess
it's doing your job.

Does any of this rambly stream-of-consciousness explanation help?
From: Dr. Edmund Weitz
Subject: Re: EVAL-WHEN in REPL
Date: 
Message-ID: <m3d71nnf22.fsf@bird.agharta.de>
Kent M Pitman <······@world.std.com> writes:

> ···@agharta.de (Dr. Edmund Weitz) writes:
> 
> > This is what I get in LispWorks (and Allegro, CLISP, and ECLS),
> > 
> >   CL-USER 7 > (defparameter *foo* 3)
> >   *FOO*
> >   
> >   CL-USER 8 > (eval-when (:compile-toplevel) (setq *foo* 4))
> >   
> >   NIL
> >   
> >   CL-USER 9 > *foo*
> >   3
> >   
> > and it is pretty much what I expected. However, CMUCL (and SBCL)
> > behave differently:
> > 
> >   * (defparameter *foo* 3)
> >   *FOO*
> >   * (eval-when (:compile-toplevel) (setq *foo* 4))
> >   
> >   NIL
> >   * *foo*
> >   4
> 
> [Detailed and enlightening answer deleted]
> 
> Does any of this rambly stream-of-consciousness explanation help?

Thanks Molly... :)

It does. It actually confirms my initial reaction[1] to CMUCL's
behaviour - I simply thought it was wrong. My problem was (and still
is) that I can't find a place in the CLHS where I can point at,
otherwise I would have sent this as a bug report to the CMUCL
maintainers instead of asking for clarification here. But maybe I
should just read it again.

And now for something completely different: In 3.2.3.1 of the CLHS
there's a line 'plus .5 fil' that I think shouldn't be there (looks
like a remnant from the TeX sources). I found it in v3 and v6. Are you
still the right one to report stuff like this to or should I rather
inform Xanalys about it?

Thanks again,
Edi.

PS: I think the whole compile-time/load-time stuff could be a good
subject for one of the books you're planning. I haven't seen a good
explanation of this topic in any of the CL books I've read so far.

[1]: To put it in my own words: The situations :COMPILE-TOPLEVEL,
     :LOAD-TOPLEVEL and :EXECUTE form a partition (in the mathematical
     sense) of all possible situations during which a top level form
     can be processed, i.e. no other situation can occur, and the
     three are mutually disjoint. A form is either COMPILE-FILEd and
     then LOADed in which case :COMPILE-TOPLEVEL and :LOAD-TOPLEVEL
     apply successively, or it is entered into the REPL (by typing it
     in or by LOADing source code) in which case :EXECUTE applies.

     If an EVAL-WHEN doesn't specify any of these three situations, it
     is as if one had written

        (EVAL-WHEN
          (:this-situation)
            NIL
          (:other-situations)
            ...)

     instead (just for clarification - I know that this syntax isn't
     correct).
From: Pierre R. Mai
Subject: Re: EVAL-WHEN in REPL
Date: 
Message-ID: <87ofl6ooll.fsf@orion.bln.pmsf.de>
···@agharta.de (Dr. Edmund Weitz) writes:

> and it is pretty much what I expected. However, CMUCL (and SBCL)
> behave differently:
> 
>   * (defparameter *foo* 3)
>   *FOO*
>   * (eval-when (:compile-toplevel) (setq *foo* 4))
>   
>   NIL
>   * *foo*
>   4
> 
> Now, I thought I was finally coming close to understanding EVAL-WHEN,
> but I might be confused again.

This is just variant behaviour (i.e. an ANSI conformance bug) by CMU
CL, which has been fixed in the current sources, and will be included
in the upcoming release.  Current binaries which include that fix
(among many, many others) are available for most supported platforms
in the binaries/ (current binaries in release-similar form) and
experimental/ (for new ports, and other less official binaries)
directories of the CMU CL download machine and current mirrors.

The completely correct handling of EVAL-WHEN in CMU CL has been a
somewhat involved issue, for various reasons.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein
From: Dr. Edmund Weitz
Subject: Re: EVAL-WHEN in REPL
Date: 
Message-ID: <m3adwqohoy.fsf@bird.agharta.de>
"Pierre R. Mai" <····@acm.org> writes:

> This is just variant behaviour (i.e. an ANSI conformance bug) by CMU
> CL, which has been fixed in the current sources, and will be
> included in the upcoming release.

Thanks for the info. So it was actually non-conforming behaviour and I
simply spent too much time thinking about why it might be
conforming... :)

> Current binaries which include that fix (among many, many others)
> are available for most supported platforms in the binaries/ (current
> binaries in release-similar form) and experimental/ (for new ports,
> and other less official binaries) directories of the CMU CL download
> machine and current mirrors.

Are they binary compatible with a 18c release that was downloaded
probably half a year ago? (I mean will they be able to load my old
.x86f files?) I'm somewhat hesitant to install the new binaries just
to find out that I have to recompile Garnet, CLOCC, clg and what else
I might be playing with at the moment.

Thanks,
Edi.
From: Pierre R. Mai
Subject: Re: EVAL-WHEN in REPL
Date: 
Message-ID: <87vgfellg6.fsf@orion.bln.pmsf.de>
···@agharta.de (Dr. Edmund Weitz) writes:

> > Current binaries which include that fix (among many, many others)
> > are available for most supported platforms in the binaries/ (current
> > binaries in release-similar form) and experimental/ (for new ports,
> > and other less official binaries) directories of the CMU CL download
> > machine and current mirrors.
> 
> Are they binary compatible with a 18c release that was downloaded
> probably half a year ago? (I mean will they be able to load my old
> .x86f files?) I'm somewhat hesitant to install the new binaries just
> to find out that I have to recompile Garnet, CLOCC, clg and what else
> I might be playing with at the moment.

No, current binaries and the upcoming release, will not be binary
compatible with 18c, i.e. you will need to recompile.  This is mostly
due to a related fix which changes handling and hence the expansion of
defvar/defparameter.

Regs, Pierre.

-- 
Pierre R. Mai <····@acm.org>                    http://www.pmsf.de/pmai/
 The most likely way for the world to be destroyed, most experts agree,
 is by accident. That's where we come in; we're computer professionals.
 We cause accidents.                           -- Nathaniel Borenstein