I have been learning common lisp and am a newbie, using the great book Practical Common Lisp. There is a once-only macro in the book that I spent a lot of time figuring out how it works. Even after I figured out how it works, I still have to look at it and think for a while every time I come across it.
I tried to write myself a new one, like this:
(defmacro my-once-only ((&rest names) &body body)
`(let (,@ (loop for n in names collect `(,n (eval ,n))))
,@body))
I don't use any gensyms; I just use the original name. They get shadowed by the value of the form they represent and the forms are evaluated only once. For now I can't see any problems in this implementation. Does it have any problems, like potential abstraction leak?
Here is the original once-only macro:
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,@(loop for g in gensyms collect `(,g (gensym))))
`(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
,@body)))))
I tried to expand my own version
(macroexpand-1 '(my-once-only (start end)
`(do ((x ,start (1+ x)))
((> x ,end))
(print x))))
It gives:
(LET ((START (EVAL START)) (END (EVAL END)))
`(DO ((X ,START (1+ X))) ((> X ,END)) (PRINT X)))