lisp-transformer

R6RSには、伝統的なマクロを作るためのlisp-transformerが紹介されている。

lisp-transformer自体はR6RS標準には含まれていない。単に、syntax-caseの例として紹介されているに過ぎない。
通常の人間にとっては、define-syntaxの次にはsyntax-rulesを書くというのがイディオムとして定着しているので、lisp-transformerの使い方はなかなか解りづらい。

Wikipediaを見ると、他に様々なtransformerが使えることが解る。R6RSではこのうちsyntax-caseとsyntax-rulesを定義しているし、nmoshではer-macro-transformer(ライブラリ(explicit-renaming)にて提供)が使える。
同様に、lisp-transformerも、define-syntaxの直後に書くことで使える。

lisp-transformerを使って、yuniSchemeのdefine-table構文を実装してみた。(ただし、これは同じスロット名を持つtableを宣言すると間違ったことが起こる)

(define-syntax define-table
  (lisp-transformer
    (lambda (f)
     ...

fには、(define-table ...)となるようなリストがわたるので、それをパースして適当なプログラムを返す手続きを書けば良いことになる。
nmosh特有の注意として、ちゃんとnmoshのgensymを使わないと、意図しないシンボルの衝突が発生することがある。nmoshはライブラリをキャッシュするがgensymのカウンタまでは保存しない。よって、gensymを含んだライブラリをロードする度にカウンタはリセットされるものと考える必要がある。nmosh備え付けのgensymはその問題が起きないように配慮している。
また、lisp-transformer内で使用する手続きはexpandレベルで定義されている必要がある。平たく言えば、lisp-transformerの内部で宣言するか、外部のライブラリに宣言したうえで(import (for (some library) expand))のようにしてインポートしなければならない。
実はnmoshはsyntax objectを直接オブジェクトとして処理できるので、lisp-tranformerを使わずに直接手続きを書くという裏技もある。。このような性質はR6RSでは要求されていないので、psyntax moshとnmoshの違いの一つとなっている。
例えば次のスクリプトを考える :

(import (rnrs) (for (mosh pp) expand))

(define-syntax dump
  (lambda (f)
    (pp f)(newline)))

(dump x y z)
$ nmosh t.scm
(#[identifier] ←リストが出力される
 #[identifier] ← xを表す構文オブジェクト
 #[identifier] ← y
 #[identifier])  z

$ mosh t.scm
#[unknown] ← リストでないことに注意

 Condition components:
(変換器が未定義値を返したのでエラーが起こる)