使い捨てのシンボル
適当なシンボルを生成したいときは、シンボルを生成してくれる手続きgensymを使えばいい。
gosh> (gensym) #:G1
とまあ、こんな感じ。
このgensymと伝統的なマクロを使って、id:yagiey:20100127:1264595558で定義したmy-orを書き直してみると
(define-macro (my-or . args) (cond ((null? args) #f) ((null? (cdr args)) (car args)) (else (let ((tmp (gensym))) `(let ((,tmp ,(car args))) (if ,tmp ,tmp (my-or . ,(cdr args))))))))
こうなる。
tmpはマクロ展開の外で束縛されていてもかまわない。このマクロを使ってみると
gosh> (define tmp 2) tmp gosh> (my-or #f tmp) 2 gosh> (macroexpand `(my-or #f tmp)) (let ((#0=#:G5 #f)) (if #0# #0# (my-or tmp)))
こうなる。
ここで出てきているtmpは、はじめにdefineしたtmpで、マクロ定義内での(gensym)の結果が束縛しているtmpではない。
マクロ定義内でのtmpは、(手続き言語的な言い方をすれば)変数名を格納した変数。
tmpは準クオート内のletでアンクオートされた結果、それが参照する値(gensymbで生成されたシンボル #:G○)になり、#:G○ が(car args) すなわち #f に束縛されるようなソースコードを返す。
アンクオートにより、マクロ定義内でのtmpは展開後には見えなくなることがmacroexpandから分かる。
シンボルが何となく分かってきた。
間違いを恐れずに言うと、「評価すると変数名になるような値」かな。
評価すると変数名になるから、変数名を扱うような、つまりメタプログラミングにはシンボルという種類の値がよく出てくるんだろうね。
さて、話はかわるけど、gensymで生成したシンボルは特別で、eq?で同じ(だと期待される)シンボルと比較しても#fになるのだそうだ。
例えば、gensymで生成していないシンボルの場合は
gosh> (define foo 'hoge) foo gosh> (eq? 'hoge foo) #t gosh> (eq? foo foo) #t
となる。だけどgensymで生成したシンボルは
gosh> (define bar (gensym)) bar gosh> bar #:G9 gosh> (eq? '#:G9 bar) #f gosh> (eq? bar bar) #t
このことが、上記のmy-orとどう関係あるかは、よく分からないけど。