Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Q: example usage of EVAL-WHEN

38 views
Skip to first unread message

Xenophon Fenderson the Carbon(d)ated

unread,
Oct 28, 2000, 2:09:58 PM10/28/00
to
The entries in the CLHS and CLtL2 leave me confused as to the use of
EVAL-WHEN (especially CLtL2's use of "compile-time-too" mode, etc.)

I have text parsing code that looks something like the following:

(defparameter *the-default-macro-table* (make-hash-table))

;; here is some code to initialize *the-default-macro-table*
(setf (gethash #\a *the-default-macro-table*) #'process-\a)
(setf (gethash #\b *the-default-macro-table*) #'process-\b)

Where *the-default-table* is used in a class definition as the initial
value of one of its slots (a table of characters and their reader
macros).

I would like the hash table (along with its initial entries) to be
re-created each time the file is loaded.

Do I have to use EVAL-WHEN to get the behavior I want?

When should I use EVAL-WHEN?

When shouldn't I use EVAL-WHEN?

--
"Remember - if all you have is an axe, every problem looks like hours of fun."
-- Frossie in the monastery

Kent M Pitman

unread,
Oct 28, 2000, 7:11:32 PM10/28/00
to
xeno...@irtnog.org (Xenophon Fenderson the Carbon(d)ated) writes:

> The entries in the CLHS and CLtL2 leave me confused as to the use of
> EVAL-WHEN (especially CLtL2's use of "compile-time-too" mode, etc.)
>
> I have text parsing code that looks something like the following:
>
> (defparameter *the-default-macro-table* (make-hash-table))
>
> ;; here is some code to initialize *the-default-macro-table*
> (setf (gethash #\a *the-default-macro-table*) #'process-\a)
> (setf (gethash #\b *the-default-macro-table*) #'process-\b)
>
> Where *the-default-table* is used in a class definition as the initial
> value of one of its slots (a table of characters and their reader
> macros).
>
> I would like the hash table (along with its initial entries) to be
> re-created each time the file is loaded.

That's what DEFPARAMETER is supposed to do. (Or, more properly, it
will do it each time it's executed, ... which at top level of a file
will be each time the file is loaded.)

> Do I have to use EVAL-WHEN to get the behavior I want?

I wouldn't think so. (I didn't try the code, but what you describe doesn't
sound like something that needs it.)

> When should I use EVAL-WHEN?

Well, for example, when the compile-time behavior will need to rely on
the value. For example, if you wanted to write a macro whose behavior
depended on seeing these values, the macro would be expanded at
compile time. So you'd have to force them to also evaluate soon enough
that the values would be accessible at compile time.

The key to understanding EVAL-WHEN's compile-time-too aspect is to keep
an eye out for operators and evaluation contexts that depend on compile
time or other early "times", like "read time". Programming is a continual
cascade of decision making from design time to coding time to read time
to macro expansion time to load time to execution time, and in some situations
there are other times in there as well. For every action that happens, there
is a "time" that it happens. EVAL-WHEN's job is to help you recognize that
a certain action is going to happen too early or too late and to accelerate
or delay or share that action in such a way that it happens at the right
time (and, in the case of file compilation, in the right place) for the
other computations that will depend on it. You can't understand EVAL-WHEN
independently of what effects happen later in your program that might depend
on them.

In the case you cite, DEFPARAMETER evaluates its initial value at load time.
That means that nothing that follows better use that variable's value at
compile time or you need EVAL-WHEN to fix it. Then you do assignments.
These happen at load time. That means DEFPARAMETER did its action just in
time and the assignments to the hash table are going to find a hash table
to put things in, so you're still cooking. The initial value of the slot
isn't going to happen until the object is instantiated, and that's not going
to happen until compile time, so that's ok. Now, DEFPARAMETER also has a
compile time component, which is to declare the parameter name globally
SPECIAL. That means that the DEFPARAMETER has to precede all the class
definitions which will mention the variable so that they make sure to do
a special variable compilation of the name instead of a non-special variable
compilation, but you don't have to do anything other than make sure the
class files follow the defparameter file in order for things to work.

(Note that DEFPARAMETER internally does this bit of compilation magic by
calling something like PROCLAIM, which is a function whose action would
only normally happen at runtime, but if you expand the DEFPARAMETER, you'll
probably see that it hurries this proclamation up by doing, among other
things, something vaguely like
(EVAL-WHEN (:COMPILE-TOP-LEVEL :LOAD-TOP-LEVEL)
(PROCLAIM '(SPECIAL *THE-DEFAULT-MACRO-TABLE*)))
in order to force the proclamation to happen both in the compiler and at
runtime, since it is potentially needed in either place. I say "vaguely
like" because (a) this expansion will vary with implementation and (b)
probably some implementation-level system magic will be substituted and
(c) in modern CL, it now does a DECLAIM instead of PROCLAIM, which works
in a slightly stranger way, but which is best left oversimplified for my
explanation here so you won't miss my point.)

> When shouldn't I use EVAL-WHEN?

When it's not necessary. I know that sounds flip, but the point is that
you should understand what it's for and you should use it to solve such
problems when they come up. You shouldn't use it because "you think it's
a good idea" or because "you don't understand the situation and think it
might help". You should first understand the situation and then if the
situation calls for EVAL-WHEN you should use it because you know it solves
your problem. That might not fix all your problems but will leave you with
only "first order" problems; if you start to use it where you don't
understand, you may not fix the first order problem and may proceed to other,
second order, problems caused by introducing new behaviors that you don't
understand on top of old ones you didn't understand either and the mess
will get worse. So, my advice is to ask only the first of these two questions.

Rainer Joswig

unread,
Oct 29, 2000, 10:19:47 AM10/29/00
to
In article <w4obsw4...@lovecraft.irtnog.org>, xeno...@irtnog.org
(Xenophon Fenderson the Carbon(d)ated) wrote:

> The entries in the CLHS and CLtL2 leave me confused as to the use of
> EVAL-WHEN (especially CLtL2's use of "compile-time-too" mode, etc.)
>
> I have text parsing code that looks something like the following:
>
> (defparameter *the-default-macro-table* (make-hash-table))
>
> ;; here is some code to initialize *the-default-macro-table*
> (setf (gethash #\a *the-default-macro-table*) #'process-\a)
> (setf (gethash #\b *the-default-macro-table*) #'process-\b)
>
> Where *the-default-table* is used in a class definition as the initial
> value of one of its slots (a table of characters and their reader
> macros).
>
> I would like the hash table (along with its initial entries) to be
> re-created each time the file is loaded.

Above code should do it. Put it in a file and load it.
Note the use of defparameter - defvar would
not change the value once it is defined.

> Do I have to use EVAL-WHEN to get the behavior I want?

No.

> When should I use EVAL-WHEN?

ANSI Common Lisp defines at what time which computations
are done with what effect (well, sort of).

Usually computation can be done at:

- read time. This is under the control of the Lisp "reader".
- compile time. This changes the environment which the compiler
uses. In Common Lisp the compiler can be extended
in Common Lisp - for example by writing Macros.
Usually you don't want to execute source code at compile time.
But in Common Lisp, for example macros are available and executed
at compile. If you want for example, that a macro can
use a certain function at compile time, you either have
to load the definition before or define it at compile-time
via EVAL-WHEN :compile-toplevel .

If you write (defun foo () ), the compile just generates
code for it. You can use FOO after loading the compiled
file.

But if you write (defmacro bar () ), the compiler also
notes the macro BAR in its compilation environment and
it will happily transform any usage of the macro BAR later
in the file.
If a file compiler is finished compiling a file, the
definition of BAR usually is removed - it is then again
available, after loading the compiled file.

So, if you want to use FOO in BAR:

(defmacro bar () (foo)) ; the macro is using FOO, instead
; of transforming code into something using
; FOO

You have to tell the compiler about FOO:

(eval-when (:compile-toplevel) ; add other clauses if necessary
(defun foo () ))

- load time. Same applies here. Usually code does not run
at load time. But one might want to change that, for example
to automatically restore objects that can't be
directly stored to disk.
- run time. Yeah, here we go...

> When shouldn't I use EVAL-WHEN?

When you don't need it. ;-) Use it only where necessary.
Unneeded EVAL-WHENs are a source of confusion.

--
Rainer Joswig, Hamburg, Germany
Email: mailto:jos...@corporate-world.lisp.de
Web: http://corporate-world.lisp.de/

Erik Naggum

unread,
Oct 30, 2000, 5:45:37 AM10/30/00
to
* xeno...@irtnog.org (Xenophon Fenderson the Carbon(d)ated)
| I have text parsing code that looks something like the following:
|
| (defparameter *the-default-macro-table* (make-hash-table))
|
| ;; here is some code to initialize *the-default-macro-table*
| (setf (gethash #\a *the-default-macro-table*) #'process-\a)
| (setf (gethash #\b *the-default-macro-table*) #'process-\b)

If these pieces of code are always executed together, it would
perhaps look better if you did something like this:

(defparameter *the-default-macro-table*
(let ((hash-table (make-hash-table)))
(setf (gethash #\b hash-table) #'process-\b)
(setf (gethash #\b hash-table) #'process-\b)
hash-table))

| I would like the hash table (along with its initial entries) to be
| re-created each time the file is loaded.

This is what defparameter does.

| Do I have to use EVAL-WHEN to get the behavior I want?

No.

| When should I use EVAL-WHEN?
|
| When shouldn't I use EVAL-WHEN?

These are almost impossibly broad questions.

You should use eval-when when evaluation is to occur at other times
than the defaults. You should not use eval-when when the default is
sufficient or if you don't understand when to use eval-when...

#:Erik
--
Does anyone remember where I parked Air Force One?
-- George W. Bush

0 new messages