[Anthy-dev 2916] Re: [r5rs] Guard

Back to archive index

Jun Inoue jun.l****@gmail*****
2006年 4月 27日 (木) 17:37:59 JST


ヤマケンさんの立場はよくわかりました。それなら文句ありません。
 
compaction を終えたら暫くは離れるつもりなので、macro の現状をちょっと
説明しておきます。

> 一方、SRFIについては必要性が無い限りCで実装する事には積極的に反
> 対です。今までに書かれたmodule-srfi*.cはSchemeでは書けなかったか、
> pure Schemeでは著しく効率が落ちるものだけです。SRFI-34もCで書き
> たいわけではなく、実装時点でマクロを利用できなかったからああなっ
> ただけです。
>
> 太田さんが書きかけのmodule-srfi1.cは上記に当てはまりませんが、
> uimではSLIB実装の方を使うつもりである事は最初期から繰り返し表明
> しています。効率のために一部のprocedureをCで独自実装する事は考え
> られますが。
>
> 井上さんがマクロを実装してくれたおかげで既存のSchemeによるSRFI実
> 装を流用できる幅が大きく拡がったわけですが、今あるmodule-srfi*.c 
> は効率面重視の選択肢としてそのまま併存させるつもりです。一定の品
> 質も確保できていると考えているし、マクロはいらないがSRFI-8は欲し
> いというような場面もあると思うので。

Macro は効率最悪です。実効速度は相対的に結構速い方ですが、そもそも要求
されてる操作自体がかなり高価な上に、cell をバンバン消費します。
Template に出現する pair と vector はほぼ全部、展開回数 + compile 回数
だけ copy されます。同じ所を複数回実行すればそれだけ展開し直されるので、
hot spot で macro を使うのは自殺行為です。

この impact を多少和らげる方法として source list に破壊的に memoize す
ることが考えられます。展開回数が一回と指定されてる伝統的 macro を実装
するなら、これをするのは必須です。*多分* 普通に scm_eval() で代入する
だけでいけると思いますが、何か見落としてそうな気配がします。今のところ
気づいている問題点・注意点は

1) 展開結果が pair (list) でないときは memoize 不可。(ENTYPE すること
になるから。storage-compact では ENTYPE は基本的に使えない。)

2) Eval で分岐が増える。痛い。→macro を使う部分だけに対価を払わせる形
で元の form を渡す手段は無いものか

3) 今のところ expander が返す object は完全に新しく allocate されたも
のであることが保証されている。TR_* みたいなものでこの保証を崩すならば 
memoization で破壊されうる箇所 (operator 位置の pair) だけは copy され
ていることを確認しないといけない。ただし TR_* で cell をケチろうとして
も、普通の template は symbol だらけ = far symbol に置き換える必要のあ
る container だらけなので、あんまり得ではない。

4) scm_p_eval() の入力を deep copy する必要あり。あるいは eval! が仕様
だと言い張る。

5) syntax-case と共存不可能かもしれない。

(define f (let-syntax ((macro (lambda () (syntax-case [...]))))
             (lambda args (macro some-args))))

(f 0)
(f 1)

一回目の呼び出しで args = (0) な環境を捕捉して、その条件の元で展開され
た form が代入されるので、二回目の呼び出しでも (macro some-args) のあっ
た箇所は args = (0) な環境で評価されてしまう。

syntax-rules だと、環境の中身は無視して frame の数を覚えるだけでいいの
で、この問題は起きない。一方、syntax-case などの unhygienic macro だと、
定義により env を辿っていくだけでは見つけられない環境を参照させられる
可能性があるので、env *object* を捉えるか、文字どおり rename をしない
といけない。後者は SigScheme では無理。ただ、伝統的 macro では正に env
object を掴むのが求められている semantics なので、問題なし。
syntax-case の環境捕捉がそれより柔軟だとまずい。


Memoization の他に選択肢の成り損ねとしては:

a) 引数 list の address を key にして hash table に格納する

→ Tight loop の中で macro を呼ぶなら恩恵はある。その代わり table 自身
が memory を食うのと、展開前の form を捨ててないので節約度は破壊代入よ
りは低い。eval を使わないならね。

b) Far symbol を read 可能にして uim compile 時に片っ端から展開する

→話にもなりません。Gauche とか bigloo 使った方が早いし、展開する
macro としない macro を見分けられません。


それと、展開された後に残る wrapped identifier の lookup はやや遅くなり
ます。

もう一つ。
(let-syntax ((macro ([...])))
  macro)
みたいにして macro を本来の scope の外に放り出して使ったり、
(let-syntax ((macro (let ((a 0)) (syntax-rules [...]))))
  (macro foo))

みたいに let/define-syntax と syntax-rules の間に binding form を挟ん
だりすると scheme level で予測不能動作をします。Crash はしないと思いま
すが。名前空間を分けた方がいいかもしれませんが、多分高くつきます。



Anthy-dev メーリングリストの案内
Back to archive index