Nikodemus Siivola <tsiivola@...> writes:
> On Wed, 26 May 2004, David Steuber wrote:
>
> > I'm trying to figure out why a macro that I am working on does not
> > work. I'm trying to be nice and using gensyms to prevent variable
> > capture. The problem is, the gensyms don't seem to get bound. I've
> > narrowed this down to a fairly simple case that shows the difference
> > between a regular variable and one that is in the form of a gensym:
>
> I'm hoping that code is worth more words here:
>
> (eq #:foo #:foo) => NIL
> (eq (gensym "FOO") (gensym "FOO")) => NIL
This explains why my narrowed down test case didn't work. I got to my
test case by being clever with Slime. In the lisp buffer where source
is, you can do C-c RET to do a macro-expand-1 on an expression (if it
is a macro call). This opens up a *Slime Description* buffer where
the macro expansion is shown, complete with the gensyms (because of
the standard idiom used that I thought was using). I then tried to
C-x C-e that expansion and of course got the errors I was complaining
about for the reasons that have now been made clear to me.
The errors looked like (maybe I read too quickly) the errors I was
getting when I was trying to test the macro by having a call to it in
my lisp buffer where I did a C-x C-e to evaluate the macro call.
> (let ((foo (gensym "FOO")))
> (eq foo foo)) => T
Well that restores some of my faith.
> So:
>
> (defmacro with-frob (var &body forms)
> (let ((n-var (gensym "VAR")))
> `(let ((,n-var ,var))
> (unwind-protect
> (progn
> (setf ,n-var (frob ,n-var))
> ,@forms)
> (unfrob ,n-var)))))
I wasn't being quite that fancy. I had something like:
(defmacro foo (&rest args &key a-key b-key)
(let ((ac (gensym)))
`(let ((,ac (copy-list args)))
(remf ,ac :a-key)
(a-function ,@ac))))
At least as best I can recall that is what it looked like. I have
destructively altered the code since to get a working macro that I
think is hygienic by virtue of useing lexical scoping and only
specific free variables but that does not use gensyms.
> A further word of explanation: the printed representation of uninterned
> symbols is not "read-eq". Each time the reader encounters #:foo, an new
> uninterned symbol is made, of no relation to any previous #:foo. This is a
> crucial property, since it guarantees the hygiene you're after.
Ok, that makes sense (now). But what about during macro expansion?
Surely that doesn't go through the reader again in the normal case of
calling a macro. I probably didn't do what I thought I did even
though I modeled my use of gensyms from an example in On Lisp.
> Hence, to use uninterned symbols in a macro you need to stuff the symbol
> into a variable and splice it into the expansion. Of course, this means
> that reading back in the printed representation of the macroexpansion
> for evaluation will not work.
Which is why my method of trying to debug the macro lead me down the
wrong path. I was more confused than Jessica Simpson being given a
tour at the White House by the Secretary of the Interior.
> If you need to do that for debugging or some other reason, you cannot use
> uninterned symbols, but may want to investigate GENTEMP instead.
I'll keep that in mind. Thanks.
The final real form of my macro looks like this:
(defmacro skill (name key-ability &rest args &key
subskill trained-only armor-check-penalty
check action try-again special synergy
restriction untrained)
"Create skills based on the skill structure and stick it in the *skills* hash table."
(declare (ignore trained-only armor-check-penalty check action try-again special synergy
restriction untrained))
(if (null subskill)
`(setf (gethash ,name *skills*) (make-skill :name ,name :key-ability ,key-ability ,@args))
(let ((ac (copy-list args)))
(remf ac :subskill)
`(dolist (ss ',subskill)
(let ((sn (format nil "~A (~A)" ,name ss)))
(setf (gethash sn *skills*) (make-skill :name sn :key-ability ,key-ability ,@ac)))))))
Final until I change my mind anyway. Hopefully this is hygienic at
least for pedagogical purposes if not outright necessity. The macro
is intended to be called as a top-level form, but you never know how
things will really get used.
Thanks, everyone.
--
I wouldn't mind the rat race so much if it wasn't for all the damn cats.
|