クロージャについてもう少し考えてみました。

昨日のエントリRui さんからコメントを頂きました。ありがとうございます。

私もなんだか同じイメージを持ってます。静的スコープでオブジェクトの寿命が永遠(無限エクステント)というのがまず初めにあって、その結果クロージャがあるという印象。

私が漠然と感じているクロージャへの解釈を、一言で表現されている様に思いました。

ここで無限エクステントに触れられていますが、実は私も、先のエントリを書いたとき、クロージャを副次的に作り出している仕様の一つとして、無限 (実装上は有限になるのでしょうが) エクステントを挙げようとは思ったのです。
しかし、レキシカルスコープとブロックの階層化 (手続きをトップレベル以外でも定義可能とか、lambda はどこでも書けるとか) を挙げることで、必然的に無限エクステントが求められることになると考えて、そちらを優先したのでした。
そして、その考えが、

例えば、レキシカルスコープであって、トップレベル以外で手続きが定義可能 (まあ、lambda 式が書ければ良い訳ですが) なことから、定義部分から可視範囲にある自由変数へのアクセスが可能なところとか。
それを実現するために、手続き生成時点のスタックフレームを、生成された手続きが利用可能な期間中は保持しておくところとか。

に現われています。
ここで、特に「生成された手続きが利用可能な期間中は」と限定しているのは、Schemeの実装におけるスタックフレーム(Draft) に書かれている内容に影響されています。更に Gauche ではスタックフレームをヒープに移動したりもしている様ですね。

ただ、これは私の思考の流れがそういう向きを向いていただけで、無限エクステントを挙げた方がスッキリする様に思います。

この部分は、クロージャの本質である (と私が勝手に考えている) 状態の保持に密接な関係があるところですから、先ず、

  • フレーム内のオブジェクトの存続期間が無限であって、フレームを参照しているクロージャが全て回収されるまでその存在が保証されている。

ことが明確になる考え方を土台とした方が筋が良いと考え直したからです。

フレームの扱いに関しては、`環境' として、`継続' やクロージャに大きく影響するところですし、もっと明確にイメージを掴んでおきたいと感じるところですね。

魔術師本が読めません。

ちっとも魔術師本 (SICP) を読んでいません。実は訳あって本自体が手元に無く、読み進められない状況になっています。もう一ヶ月になりましょうか…… (言い訳モードになってます。誰にともなく……)

来週辺り手元に戻って来るかもしれないと、淡い期待を抱きつつ、クロージャの事など考えています。

クロージャについて、どんどん発散して行くなあ。

色々考えていることを整理してみたいと思ったのですが、意に反して、どんどん発散してしまいます。こんなときはまだ整理できる段階ではないということで、発散するままに書き連ねてしまうことにしてみました。

先のエントリで少し触れましたが、私は、クロージャの本質を「状態の保持」と捉えています。
この「状態」とは、Scheme 的に言えば、`環境' と言うことになりましょうか。

スコープ

そう言う意味では、自由変数へのアクセスをクロージャの定義 (生成) 時点のスコープで行なうのか、動作 (評価) 時点のスコープで行うのかという話については、枝葉でしかないのでは、と思っています。
いえ、自由変数へのアクセスがクロージャの定義 (生成) 時点のスコープで行なわれることには大きな意味があるのですが、それは「定義時点のものか、動作 (評価) 時点のものか」という視点で考えるものではなく、「一つの環境に特定できるかどうか」という視点で考えるべきものじゃないのだろうかと思うのです。

つまり、ダイナミックスコープな環境でクロージャが生成できないのは、クロージャが評価される文脈で、クロージャから見える環境が異なってしまうことに問題があるからだと思う訳です。
逆を言えば、ダイナミックスコープであっても、生成されたクロージャが存在している間、特定の環境にのみ結び付けられる仕組みがありさえすれば良いと言えるのかもしれません。

また、コードブロックの上位階層に遡って自由変数にアクセスすることについても、必ずしも必要なものではないのでは? とも思っています。そう考えると、自由変数は抜きにして、束縛変数だけで環境を構成するというのもアリかと思いました。クロージャの作り易さという面をスポイルすることにはなってしまうかもしれませんが。

オブジェクトとの類似

この、状態 (環境) の保持という面では、`継続' も同じかと思っています。しかし、継続の場合は、その目的、役割がプログラムの実行制御 (状態遷移と言った方が良いのかな) のためのものであり、クロージャとは異なってくるのでしょう。
あ、クロージャも、コールバック手続きとして使われる様なケースでは、継続と似た役割を持つことになると言えるのかもしれませんが。

そして、状態 (環境) の保持が本質であると考えると、やはりオブジェクト指向で言うところのオブジェクトやインスタンスの単純な一形態と言えるのではないかと思うのです。
インスタンスごとに状態 (環境) として変数群を保持し、それらに作用する、或いはそれらに基く処理を行なう手続きを持つ、立派なオブジェクトと言えると思います。
内部状態となる変数に手続きを保持して適用することも可能な訳ですから、インスタンスごとに手続き自体の振る舞いを変えることも可能ですね。
クロージャ自身は一つの手続きとなるため、メソッドを複数持てるオブジェクトに比べて貧弱に思われるかもしれませんが、同じ状態 (環境) に対して複数のクロージャを生成することができるので、複数のメソッドを持つオブジェクトと同等の振る舞いを持たせることも可能ですし。複数クロージャの管理には工夫が必要になるでしょうが。

所謂オブジェクト指向言語による様々なサポートを全て実現できるというつもりはありません (工夫すれば何とかなるでしょうが、その工夫のコストを掛けることが妥当でないという判断はありでしょう) が、仰々しいオブジェクト設計とは別に、ある局面でのみ簡単に独立したオブジェクトを導入したい、といったときには十分に有効かと思います。

ブロックとの違い

コレクション操作、イテレータなどの高階関数クロージャを渡すときには、状態の保持が必要ない場合もありますね。個人的には、この様なケースではクロージャではなく単なるブロックと考えているのですが、特に区別する必要は無いのでしょうね。
高階関数に渡すときでも、状態の保持が必要な場合も当然あって (コレクションの要素を数え上げるとか)、その様な場合にはクロージャ、そうでないときはブロックと区別することに意味は余り無いでしょうし、実装での区別も無いでしょうから。
Ruby だとブロックを渡すときと Proc オブジェクトを渡すときで違うと言えるのかなあ。

トラックバックありがとうございます。

何と、結城さんからトラックバックを頂いてしまいました。

メモ。

ということなんですが、これはきっと、higepon さんと同じで、

[gauche][scheme] 識者のコメントが楽しみ。

と言うことなのでしょう。

なので、既にコメント頂いている Rui さん以外の識者の方々、何卒宜しくお願い致します。;-p