with-gensyms

clispのgen-symsマクロは、生成するシンボル名が第一引数で、gen-symsで生成されるシンボルが格納される変数名は、第二引数以降が該当するみたい。

(defmacro (prefix &rest sym-names) &body body)
みたいな、シグネチャーで定義されているみたい。
実践Common Lispで前提としているwith-gensymsマクロは、
(defmacro (&rest sym-names) &body body)
というシグネチャーとしているようだった。他の処理系では、こうなってるのかな。

http://d.hatena.ne.jp/smeghead/20090517/pcl


CLISPには拡張マクロとして用意されてるんだ・・・。知らなかった。
実践Common Lisp(僕は持ってないので確認してませんが)に載っているマクロは、引数を見る限りポールグレアムが書いたやつそのままみたい(他の処理系でこの形が採用されているかは知らない)。このマクロは便利なわりに結構単純で、

CL-USER> (defmacro with-gensyms (syms &body body)
           `(let ,(mapcar #'(lambda (s) (list s '(gensym))) syms)
             ,@body))

のように定義できます。
で、CLISPで用意されている拡張マクロ(with-gensyms)とやらの仕様は、

Similar to its namesake from Paul Graham's book “On Lisp”, this macro is useful for writing other macros:

(with-gensyms ("FOO-" bar baz zot) ...)
expands to
(let ((bar (gensym "FOO-BAR-") )
(baz (gensym "FOO-BAZ-"))
(zot (gensym "FOO-ZOT-")))
...)

31.11. Additional Fancy Macros and Functions

とのことですので、このように定義できるんじゃないかと。*1

CL-USER> (defmacro with-gensyms2 ((prefix &rest syms) &body body)
           (labels ((get-prefix (s)
                      (concatenate 'string prefix (symbol-name s) "-") ))
             `(let ,(mapcar #'(lambda (s) `(,s (gensym ,(get-prefix s)))) syms)
               ,@body)))

振る舞いとしてはほとんど何も変わらなくて、gensym関数に値を渡しているかどうか。

CL-USER> (macroexpand
          '(with-gensyms (a b)
            (setq a 10 b 20)))
;=> (LET ((A (GENSYM)) (B (GENSYM))) (SETQ A 10 B 20))

CL-USER> (macroexpand
          '(with-gensyms2 ("FOO-" a b c)
            (setq a 10 b 20 c 30)))
;=> (LET ((A (GENSYM "FOO-A-")) (B (GENSYM "FOO-B-")) (C (GENSYM "FOO-C-")))
;    (SETQ A 10 B 20 C 30))


gensym関数は一般的には引数なしで使うけど、文字列を渡すとそれをプレフィックスとして使うようになる。

CL-USER> (gensym)
#:G4400 ;; デフォルトだとGというプレフィックスが使われる
CL-USER> (gensym "LISP")
#:LISP4401

*1:CLISPのソース見てないので(←めんどい)一致しているわけではありません