[SikiLanguage] [式鬼言語航海日誌]
2008-01-28
2008/01/29_005921ということで、昨日の続きを少しだけ。実行プロセスについて。
あいかわらず自分専用メモということで、他の人に読まれることは考えていない酷い説明文になっています。
「昨日は手続きの種類分けだったね」
はい。普通の言語ですとFiber/Thread/Processぐらいの分類だと思いますが、式鬼言語の場合はForth的なものとしてプリミティブ命令的なMonomerとコマンド列のセットPolymerというのを考えています。で、Monomerというのは実装言語のC++で実行される関数(C++バインディング)を想定しているのですが……
「遅延評価のところで困っちまうわけだね」
遅延評価というのは『引数を実際に参照するときにその値を計算する』というもので、これがあると無限に続くストリームとか必要になる都度値を計算するジェネレーターとかを自然に活用できるようになりますので、かなり便利なものです。
ただ、『引数を実際に参照するときにその値を計算する』という遅延評価の挙動は、実装言語のC++と相性が悪いんですよね。
「何で? C++から遅延評価を呼べばいいんじゃないの?」
簡単に実装するならそういう実装でもいいのですが……色々と制限が発生するのと、思わぬ挙動になる可能性があるのが良くないですね。
まず、C++の関数の挙動ですが、基本的には『関数が呼び出されると、中のコードを実行して呼び出し元に戻る』という動作です。コルーチンみたいに動作中に中断することも、Schemeみたいに継続を渡すこともできません。関数は終了するまで抜け出せず、抜け出したあとは元に戻ることもはできません。
「まあ、そりゃそうだ。基本的な構造化プログラムの動作だわな」
で、Monomerは基本的に『C++で実行される関数を想定している』ということで、もしMonomer中で引数を処理したいときはMonomer中から引数を参照する必要があります。
「引数を参照するっつうことは、遅延評価が発生する、つうことだね」
はい、ここで遅延評価が発生します。つまりプリミティブ命令的で分割不可能なはずのMonomerの実行中に別の手続きが実行されるわけです。
「そりゃもうプリミティブじゃないね」
せめて遅延評価が発生した状態でMonomerの実行を中断できればいいのですが、実装言語にC++を使用している限りそんなことはできません。C++のメソッドにもコルーチンがあればなぁ。簡単に中断・復帰ができるのに。
で、具体的にどんな問題が出そうかというと、
- 制御の動作がかなり神経質になる。気を付けないとすぐに破綻する。
- 遅延評価の対象にPolymerを使用するのは不可。Monomerを使うか、あるいはFiber, Thread, Clothで実行して結果を引き出す必要がある。
とりあえず思い付くのはこんな感じですか。他にも色々とありそうですが。
解決方法としては、ざっと
- 遅延評価を捨てる
- Monomerの内部から(遅延評価の発生する)参照は禁止する
- Monomer実行前に、必要な引数の遅延評価を処理しておく
- C++バインディングを実装するときもPolymerの形で実装する
引数を参照するところで分割して、遅延評価を行うMonomerを挿入する - 遅延評価の対象はFiberしか許さない(Fiber以外は遅延評価しない)
- C++バインディングが中断に対応できるようにする。遅延評価前にC++の実行状態を記憶しておき、遅延評価後に状態を復元するようにする(手動でコルーチンをエミュレート。Polymer的な挙動)
といった感じかと。
遅延評価を捨てるのは無しにすると、お手軽に実装するなら3.で、C++バインディングに苦労してもらうなら2-1メインで2-2を併用(ループ処理などで)といったところでしょうか? Monomerの性質が失われますが、4.の案も毛嫌いするほどのものではない気がします。
「どれも悩ましいね」
スマートさからいうと2.なんですけど、実用上は3.でしょうしね。4.は破綻しにくいでしょうけどMonomerの性質が失われるのがちょっと……むむむ、どうしよう?
制作・著作: 野分(nowake) at fiercewinds.net (Creative Commons 表示-継承 2.1 日本)