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))))

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)))

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:

  `(DO ((X ,START (1+ X))) ((> X ,END)) (PRINT X)))

1 Answer 1


Hmm...after hours of thinking and tweaking, I finally realised where the program goes wrong. The problem of my version of once-only is that it needs to evaluate some form at expansion time, however, at macro expansion time, the value of the form may be unknown. For example,

(defun hello (x)
  (doprimes (var x (+ x 10000))
    (print var)))

if doprimes is defined with my version of once-only, then the compiler will signal an error because x is unknown during macro expansion. My once-only will need to evaluate x to finish the expansion. That is the problem of my version...


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.