From: jeff@aiai.ed.ac.uk (Jeff Dalton)
Newsgroups: comp.lang.lisp
Subject: Re: static, a.k.a. eval-once
Date: 1 Sep 92 14:08:35 GMT
Organization: AIAI, University of Edinburgh, Scotland

In article <17u2ikINNs6g@early-bird.think.com> barmar@think.com (Barry Margolin) writes:
>In article <7399@skye.ed.ac.uk> jeff@aiai.ed.ac.uk (Jeff Dalton) writes:
>>About 4 years ago, I wrote an EVAL-ONCE macro.  It appears, with
>>an explanation below.  It has been claimed that my macro isn't
>>portable because the behavior of gensyms isn't sufficiently
>>restricted when compile and load is involved.  What I want to
>>know is:
>>
>>  1. Is there a problem with using GENSYM this way?
>
>I think it should work.  While the compiler has to arrange for all
>references to a particular gensym within a binary file to be the object,
>similarly named gensyms in different binary files should refer to different
>instances.
>
>BTW, I assume you realize that V should be a gensym, i.e. it should be:

Yes.  I'm not sure how I could have missed that.

>I think your version can be simplified a bit.  The expansion doesn't need a
>LET binding:
>
>(defmacro eval-once (form)
>  (let ((var (gensym)))
>    `(if (boundp ',var)
>	 (symbol-value ',var)
>	 (set ',var ,form))))

Both this and the V problem were fixed in the "#+maybe" version:

#+maybe
(defmacro eval-once (form)
  (let ((v (gensym)))
    `(locally (declare (special ,v))
       (if (boundp ',v)
           ,v
         (setq ,v ,form)))))

I suspect (it was years ago) that what happened was that I started
with something like your version, modified it (carelessly) to get
around a bug in KCL, and then wrote the "#+maybe" version w/o realizing
that it fixed the bug involving V.

The bug in KCL was that if a gensym appeared more than once _as data_
(eg, as a quoted symbol in two different places), they would end up as
separate symbols after a compile/load.  (This bug has now been fixed,
I believe.)  However, the "#+maybe" has the problem that the uses
of comma-V as a variable and as data have to line up, which wouldn't have
worked then in KCL (I suspect).

>This looks suspiciously like the stuff we were discussing a few months ago,
>where you wanted the ability to have modifiable literals in your program.
>If that were allowed, you could do:
>
>(defstruct eval-once
>  (done nil)
>  value)
>
>(defmacro eval-once (form)
>  (let ((struct (make-eval-once)))
>    `(if (eval-once-done ',struct)
>	  (eval-once-value ',struct)
>	  (setf (eval-once-done ',struct) t
>		(eval-once-value ',struct) ,form)))))

Well, I do want to have _some_ way to do this EVAL-ONCE trick.

You're right to suspect that this is connected to our earlier
discussion.  I first brought up the static/eval-once issue on one of
the Common Lisp mailing lists shortly after I'd written my macro (ie,
some years back).  If I recall correctly, what was said then was that
gensyms weren't reliable enough but that LOAD-TIME-VALUE might do it
(somehow).  So I've had it in mind to figure out, sooner or later,
whether LOAD-TIME-VALUE did help.  Various things remind me of this --
such things as the discussion about modifying constants and (now) the
public review of the dpANS.  (The latter one works by making me try
to remember the things I was going to raise during public review,
which leads me to remember other things too.)

Anyway, modifying a quoted structure looks like one way to do
eval-once, and LOAD-TIME-VALUE looks like it might help either by
giving you a modifiable object (that isn't quoted) at load time or by
letting you modify a quoted object (because you use the 2nd argument
to LOAD-TIME-VALUE to _say_ it's modifiable.  So, using the struct
you defined, we might try:

(defmacro eval-once (form)
  (let ((v (gensym)))
    `(let ((,v (load-time-value (make-eval-once))))
       (if (eval-once-done ,v)
           (eval-once-value ,v)
         (setf (eval-once-done ,v) t
               (eval-once-value ,v) ,form)))))

But that's not enough in interpreted code.

-- jd
Xref: sparky comp.lang.lisp:2322 comp.lang.clos:758 comp.lang.misc:2793 comp.object:3337 comp.std.misc:117 comp.lang.scheme:2101 comp.ai:3244