matarillo.com

The best days are ahead of us.

9.3 F# 1.0 -- 機能コアの改善:初期化グラフ

2021-09-25 00:00:21

F# に追加された今までにない機能の最初は、強く型付けされた関数型言語でこれまで使用されていなかった類の、初期化と再帰の調整でした。 当時、OCamlはすでに、関数の再帰定義(すべての関数型言語と同様に)と、次のような再帰的に参照されるデータをサポートしていました。

let xs = 1 :: xs

上記の ::はOCamlの cons 演算子で、この宣言で値 1 の無限リストが得られます。 これを実装するには、単一アロケーションのコンスセルを作成し、末尾ポインタを、値そのものを指すように「修正」します。 しかし、この手法は値を割り当てるときにしか適用されません。 関数抽象には対応していませんし、値の構築に何らかの計算が必要な場合には適用されません。

この問題に対するF# のアプローチは、2004年半ばに最初に設計・実装され、2004年9月4日にMSR Cambridgeで、その後ML Workshop 2005で発表されました[Syme 2006f]。 この機能は、上記のOCamlの機能や、Georges Gonthierとの廊下での会話や、そして再帰的定義に可能な限り「共帰納的」解釈を与えるというアイディアに触発されたものでした。 共帰納的手法 – オブジェクト指向の解釈としての共帰納的代数を含む – は、当時の人気のある研究トピックでした。 2004年のプレゼンテーションでは、私は「リアクティブオブジェクト」のネットワークを定義する方法に焦点を当てました。

サブタイピングを忘れましょう。継承を忘れましょう。 自己参照および相互参照オブジェクトに対する制限は、MLをGUIプログラミング言語としては不便なものにしています。 ……少なくとも System.Windows.Forms のような妥当なライブラリーを駆動する場合に不便ですし、そしてライブラリーが「宣言的」になるほど悪化します。 ……C# は、暗黙のNullや「作成してから構成する」APIを使ってこれを「解決」します。MLでも同じように「解決」します。 F# はこれらの手法を許可しますが、別の解決策も提供します。……

提供した解決策は、 let rec 構成体を拡張し、関数や、再帰的に参照されるデータの直接割り当てを定義するだけでなく、計算によって生成される値とオブジェクトのグラフ構造も定義できるようにする ことでした。たとえば次のようにです。

F# を使用すると、仕様上は自分自身を参照しているように見えるけれども、自己参照は遅延された値の中に隠れているような値(関数だけでなく)を書くことができます。 それは、内部関数、その他の再帰関数、匿名の “fun” ラムダ、遅延計算、そしてオブジェクト実装式の「メソッド」などです。

その再帰は「実行時にチェック」されます。これは、バインディングの評価に伴う計算が、実際には遅延計算を取り込んでおり、それらを実行する可能性があるためです。 F# コンパイラーは遅延とサンクを挿入して、実行時の自己参照が発生した場合に例外を発生させます。

この再帰は「リアクティブ」です。これは、さまざまな入力に応答し、結果として自己を参照するような変更を行うオートマトン、 たとえばフォームやコントロールやサービスなどを定義するときにのみ使うことだけが本当に意味があるためです。 簡単な例として、メニュー項目を次に示します。これは、アクションの一部として状態の一部を出力します。

let rec menuItem =
    new MenuItem("Say Hello",
                 EventHandler(fun e -> printf "Hello %s\n" menuItem.Text),
                 Shortcut.CtrlH)

これにはコンパイラーが警告を発します。 理論上は、 “new MenuItem” コンストラクターが構築プロセスの一環としてコールバックを評価する可能性があり、 その場合は自己参照が発生することになるからです – そしてF# はこれが起こらないことを証明できません。

ML Workshopの論文は、この機能とその基礎技術 – 初期化時に「再帰的使用例外」が発生する可能性がありますが、初期化が成功すると それ以上の例外は発生しません – の歴史的な先例を紹介しています。 これは現在のF# でも時折使用されていますし、F# オブジェクトプログラミングの設計のいくつかの側面に影響を与えもしました: F# 2.0のクラス定義では、オブジェクトの構築中にサブクラスのオブジェクトメンバーを「再帰的に」呼び出す仮想呼び出しは初期化の安全性がチェックされ、 初期化が完了する前に再入が起こると例外が発生します。


インデックスへ戻る