彷徨えるフジワラ

年がら年中さまよってます

継続について考える

訳あって Scheme継続 (continuation) について調べることに。

コンパイラインタプリタの一般的な実装から推測するに、継続の実現方式の概略は:

スタック等に代表される「実行コンテキスト」情報を保存しておく。継続実施の際にはコンテキストを復旧して実行を再開。

であろうという前提で、ネットを彷徨う事暫し。

.... うーん、なんか違う ....
知りたいのは「継続」の「実現方式」に関してなのだけど、継続の話になると、どのサイトも判で押したように「継続渡し」の話になってしまう。

彷徨う過程で、Ruby における継続の実現方式が先述した「コンテキスト保存」によるものであることがわかったので、当初の推測が全く見当外れでは無いことは確認できたのだけれど、どーにもこーにも頭の中で歯車が噛み合わない。

とりあえずわかった範囲の事をあーだこーだとまとめていたら .... あ、わかったかも?

きっかけは、「call/cc 契機で呼ばれる関数に渡される current-continuation は関数」である事と、ひげぽん氏のブログでの「(エントリ記述当時の) mosh の call/cc の実装は、コード改変による継続渡しをしていない」といったような記述が頭の中でリンクしたため。

つまり、言語に依存しない「継続」の基本的な実現方式としては「コンテキスト保存」なのだけれど、Scheme の場合は、計算等価性とか、プログラムもデータも共にリストであることによる実行時改変可能性を活かして、「継続渡し」形式に改変したコードを動的に生成し、「継続渡し」における「渡される関数」を current-continuation として使用することが可能、という按配なのではなかろうか?という結論に。

この推測が合っているとすると、世の Scheme の「継続」の説明って、マインドモデルとしての「コンテキスト保存/復旧」と、実現方式 (+ 最適化方式?) としての「継続渡し」の話がごっちゃになってしまっているわけだよね? Java の文法解説の本に、いきなり JIT 実装の最適化詳細が紛れ込んでいるような印象だなぁ。

いや、ひょっとしたら生粋の Scheme 使いの人からすると、「継続渡し」による「継続」実現の最適化は末尾呼び出し最適化と同程度の「常識」だとか、Scheme の言語としての概念モデルそのものがそもそも「継続渡し」のような発想を前提としているので、理解できて当然、とかいう話なのかも知れないのだけれどね。俺、骨の髄から手続き型言語な人だからなぁ .... orz

そういえば、継続について調べている過程で「末尾呼び出し最適化により展開が実施されるので、関数末尾での再帰呼び出しは "繰り返し" (ループ) とみなす」という Lisp/Scheme 界隈のルールに出くわしたのだけれど、あれもイマイチ馴染まないなぁ。やっぱり骨の髄から手続き型言語な人だからかなぁ .... orz

それからそれから、更に細かい話になるけれど、多くの継続の説明では、以下のようなコードを例に:

(+ 1 (call/cc (labmda (cc) 2)))

call/cc により生成される「継続」は「call/cc の戻り値 2 に 1 足す処理」的な記述になっていて、最初の頃は物凄く違和感を感じた。

個人的には、「1 足す」だと「1 (= call/cc より前の式) がこれから評価される」かのような印象になってしまうので、「1 足す」という記述じゃないとイマイチしっくりこないのだけど、そんなことを言っていると例の「掛け算の順序に拘るのは是か非か」論争のあおりを食らったりするのだろうか?くわばらくわばら....