2012年4月29日日曜日

Scheme第10歩

マクロの話ファイナル。
これまでのマクロの話は、low-levelとかtraditionalとか言われているタイプのマクロに関するもので、今では新しいマクロ定義の方法がある。今回は新しい方のマクロについて。

もう1つの新しいマクロはhygienic macroと呼ばれているもの。日本語だと衛生的マクロとか健全なマクロと訳されているみたい。新しいとは言ったものの、1998年の規格(R5RS)で定まったものらしいので、実はそんなに新しくもない。

前置きはこのくらいにして、早速使ってみる。
(define-syntax for   (syntax-rules (in to)
    ((_ x in xs body ...)
      (for-each (lambda (x) body ...) xs))
    ((_ i a to b body ...)
      (if (<= a b)
        (let loop ((i a)) (when (<= i b) body ... (loop (+ i 1))))
        (let loop ((i a)) (when (>= i b) body ... (loop (- i 1))))
      )
    )
  )
)
ここで定義したはforというマクロは、以下のように使用する。
(for v in '(foo bar baz) (print v))
(for c 1 to 10 print c)
(for n 100 to 10 print n)
上から順にfoo, bar, bazと表示。1, 2, ..., 10と表示。100, 99, ..., 10と表示する。

さて、いよいよマクロ定義の中身を見てみよう。
最初のdefine-syntaxは、define-macroの新型みたいなもの。次のforが定義したいマクロの名前。ここまではいいとして、問題はその次のsyntax-rules。
syntax-rulesの第1引数は、シンボルのリスト。ここで列挙したシンボルは、第2引数以降のパターンマッチの際、予約語として扱われる。
syntax-rulesの第2引数は、((パターン) 置換テンプレート)の形。指定したパターンにマッチしたら置換テンプレートを適用して得られたフォームに置換することを意味する。
パターンの中では、予約語は予約語そのものにマッチする。また、'...'は 0個以上の可変個数のフォームにマッチする。そしてそれ以外は、任意の1つのフォームにマッチする。
上記の例ではパターンの先頭に'_'があるが、これも単なる変数の1つでしかない。しかも、結局はマクロ名forにしかマッチしないので、 '_'から得られる情報は実質ゼロ。そんなどうでもいい変数の名前として '_'を使用する習慣があるようなので、今回はそれに倣ってみた。
置換テンプレートの中身は、ほとんど見た目通り。パターンの中で使用した変数は対応するマクロの実引数に置き換えられ、それ以外はそのまんま。これまでのlistやquoteを駆使してきたマクロと比べると、格段に見やすい。
syntax-rulesの第3引数以降は、 第2引数と同じ書式。要するに、複数の置換ルールを定義できるのだ。上記のforの例では、inを使う形式とtoを使う形式をまとめて定義している。

ちなみに、hygienicと呼ばれるからには単なる便利なラッパーというわけではない。新しい方法で定義したマクロでは、明示的にgensymを使わなくても自動的に名前の衝突を避けてくれる。

0 件のコメント:

コメントを投稿