Path: utzoo!utgpu!jarvis.csri.toronto.edu!cs.utexas.edu!uwm.edu!uakari.primate.wisc.edu!aplcen!uunet!mcsun!ukc!edcastle!aiai!jeff From: jeff@aiai.ed.ac.uk (Jeff Dalton) Newsgroups: comp.lang.lisp Subject: Re: PLet Message-ID: <1568@skye.ed.ac.uk> Date: 17 Jan 90 17:28:59 GMT References: <509@cs.columbia.edu> <1515@skye.ed.ac.uk> Reply-To: jeff@aiai.UUCP (Jeff Dalton) Organization: AIAI, University of Edinburgh, Scotland Lines: 77 In article lou@atanasoff.rutgers.edu (Lou Steinberg) writes: >In article <1515@skye.ed.ac.uk> jeff@aiai.ed.ac.uk (Jeff Dalton) writes: >> Unfortunately, both my macro and PLET have a problem: they create a >> new symbol each time they're expanded. Since that might be more than >> once, they don't really cause the evaluation to happen only once. Note that this is a problem for interpreted code, but not for compiled, because no further expansions of the eval-once macro will take place after compilation. Some people have suggested that my macro won't work for compiled code because the required EQ distinctions might not be maintained. I think that shouldn't be a problem, but I'm not sure the spec agrees with me. [For those who don't remember how the once-only macro worked, it used a ganerated symbol to hold the value and used boundp to see if it had been computed yet.] >There is a way to get around this: embed the MACRO definition in a let >that creates a hash table mapping macro-calls to their value. The >macro expands into something that checks the hash table to see if the >form in this call has already been evaluated. If so it just returns >that stored value, otherwise it stores the value in the hash table and >returns it. Two macro expansions are expanding the "same call" if the >macro expressions being expanded are eq. >(let ((once-only-cache (make-hash-table))) > (defmacro eval-once (&whole whole form) > `(multiple-value-bind (value foundp) > (gethash ',whole ',once-only-cache) > (if foundp value > (setf (gethash ',whole ',once-only-cache) > ,form))))) So, (eval-once (f x)) expands to (multiple-value-bind (value foundp) (gethash '(eval-once (f x)) ',#) (if foundp value (setf (gethash '(eval-once (f x)) '#) (f x)))) This is a good idea, but it may not work when compiled. >Note that we hash on the "whole" rather than the argument ("form") >because the argument might be just a symbol, which might also be the >argument of some other call to this macro. However, even this >approach can be wrong - the "whole" can be eq in two different macro >calls if the whole itself comes from a constant in another macro. That's true, and there are two other problems: 1. Compilers are allowed to "coalesce" quoted values that are EQUAL into one value. Thus, when your compiled code is loaded in, the different copies of (eval-once (f x)) may all be EQ. So the "wholes" can end up EQ even if they didn't start that way. 2. Having objects like hash tables in quoted constants may not work. Another, but correctable, problem is that DEFMACRO inside LET doesn't work in all Common Lisps. So you may have to use a global variable instead. That can be used to deal with problem (2) as well. So it seems that it isn't possible to write a once-only or Plet macro that works both interpreted and compiled in every Common Lisp. I hope I'm wrong, though. -- Jeff Jeff Dalton, JANET: J.Dalton@uk.ac.ed AI Applications Institute, ARPA: J.Dalton%uk.ac.ed@nsfnet-relay.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!J.Dalton