Racketのマクロを展開する
Racketでマクロがどう展開されているかを見るにはDrRacketのMacro Stepperを用いるか、expand系の関数を使います。
ここではexpand系の関数について説明します。DrRacketのREPL上でこれらの関数を実行すると▹#<syntax:...と表示されますので、行頭の▹をクリックしてやると展開した結果が表示されます。以下の例はREPL上からコピペしたものですが、画面表示とは多少異なっています。
まずはexpand-onceの例を示します。expand-onceはマクロ展開を1回だけ実行した結果を表示します。
code:scheme
(expand-once '(conde (nullo x) (nullo y)))
(disj (conj nullo x) (conj nullo y))
(expand-once '(run* (x) (nullo x)))
もっと展開したければ、expand-onceを重ねがけしたり、expand-to-top-formを使ったりします。
code:scheme
(expand-once (expand-once '(conde (nullo x) (nullo y))))
(disj2 (conj nullo x) (disj (conj nullo y)))
expand-to-top-formを使ったりします。
code:scheme
(expand-to-top-form '(conde (nullo x) (nullo y)))
(disj2 (conj nullo x) (disj (conj nullo y)))
(expand-to-top-form '(run* (x) (nullo x)))
(let-values (((q) (var (quote q)))) (map (reify q) (run-goal #f (conj (fresh (x) (== (quasiquote ((unquote x))) q) (nullo x)))))) expand-onceを何回もかけてくれる関数があってもよさそうな気がしますがないようなので自分で書きます。
何度も展開していると#%appみたいなのが増えてきてかえってわからなくなってきます。
この場合、7回で完全に展開されました。
code:scheme
(define (expand-nth n form)
(if (< n 1) form (expand-once (expand-nth (- n 1) form))))
(expand-nth 7 '(conde (nullo x) (nullo y)))
(#%app disj2 (#%app conj2 nullo x) (#%app conj2 nullo y))
expandを使うと一発で完全に展開してくれますが、かえってわからなくなりがちですのであまり使っていません。
code:scheme
(expand '(conde (nullo x) (nullo y)))
(#%app disj2 (#%app conj2 nullo x) (#%app conj2 nullo y))
(expand '(run* (x) (nullo x)))
(let-values (((q) (#%app var (quote q)))) (#%app map (#%app reify q) (#%app run-goal (quote #f) (#%app call/fresh (quote x) (lambda (x) (#%app conj2 (#%app == (#%app list x) q) (#%app nullo x))))))) 参考文献