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))))
(LET ((START (EVAL START)) (END (EVAL END))) `(DO ((X ,START (1+ X))) ((> X ,END)) (PRINT X)))