2013/11/07
コンパイラマクロ
Saggitariusがコンパイラマクロ導入か。
Gaucheにも実は非公開だけどdefine-compiler-macro
は備わっている。
コンパイラマクロは、Common Lispで性能が必要なアプリを書いてる時は 多用するので必要性はわかるのだけれど、個人的には言語機能としていまいちだなと感じている。
コンパイラマクロはマクロ展開時という比較的速いステージで呼ばれるため、 使える情報が限られてしまう。マクロと同様に、ソースコードの字面の情報しかわからない。
そこからできることは主にふたつ。
- 与えられている引数が即値リテラルの場合に部分評価 (コンパイル時計算できる部分を計算してしまう) ができる。
- 関数本体をインライン展開できる。
なんだけど、1については、関数引数に直接リテラルが与えられた場合しか対応できない。
例えばfoo
にコンパイラマクロがついてるとして、こんなコードがあった場合:
(define-constant *some-constant* ...) ... (let ([x (constant-expression)]) ; xはset!されてない (define (bar y) (foo x y)) ... (bar *some-constant*) ...)
コンパイラの定数伝搬パス以降であれば、fooの呼び出しが
(foo <定数> <定数>)
になっている可能性が高いのだけれど、
コンパイラマクロ展開時に見えるのはx
やy
といった変数だけなので、
コンパイラマクロ内で部分評価をすることはできないのだ。
fooをインライン展開すれば、インライン展開パスで一旦コードが展開された後、
定数伝搬パスやlambda liftingパスの後の定数畳み込みによって
ある程度コンパイル時計算が進められることが多い。でもインライン展開するだけなら
既にdefine-inline
がある。
define-inline
に比べ、インライン展開のためのコンパイラマクロは、
ほとんど同じようなコードを (手続き本体とコンパイラマクロ展開器の) 2箇所で
書く必要があり、これは大きなデメリットだ。一方だけを直してはまった
苦い思い出の数々。
なので、本当に欲しいのは、ある程度コンパイラのパスが進んで 引数の属性が分かった後に突っ込めるマクロなんだけれど、 そういうことをするにはコンパイラが使ってる内部データ構造を見せないとならないんで そのへんをまだ考えている。うかつに内部データ構造をいじれるようにしちゃうと 後でコンパイラを改良する時に足を引っ張られるから。
「この引数が定数とわかったらこういうパターンで変形せよ」みたいな ルールベースのマクロ (内部データ構造はopaqueにして触らせない) あたりが 落としどころかなあとは思っている。
ただまあ、そっちの方向で複雑にしすぎるとC++みたいなことになりかねないんで、 バランスが難しい。 (最近のC++の機能の多くは、コンパイラにどうやって安全なフックポイントを つけて自分が思うようなコードを吐かせるかって話のように見える。)
Tags: Programming, Gauche
Post a comment