matarillo.com

The best days are ahead of us.

9.2 F# 1.0 -- オブジェクトプログラミングへの取り組み

2021-09-25 00:00:21

当初から、F# は.NETで定義されているクラスやインターフェースを呼び出せました。 関数型言語なので、オブジェクトの実装には関数クロージャーと同じような式ベースの形式をサポートすることから始めるのが自然でした。 F# 1.0では、これらについて次のように説明しています1

オブジェクト式は、クラスまたはインターフェースの実装や拡張を宣言します。 たとえば、以下は .NETのIComparerインターフェースを実装するオブジェクトを構築します。

{ new IComparer with Compare(a,b) = compare a b }

OCamlのようなレコード型を使用して.NETのクラスを宣言できないか試みた後で、 私は2005年4月27日に、Dominic Cooney宛ての電子メールを通じて、F# のオブジェクトプログラミング拡張機能の設計プロセスを開始しました (彼はもうインターンではありませんでしたが、F# を使用した経験がありましたので、個人的な議論の相談役でした)。

F# のAPIをもっとOO的な方法で公開できるようにするというニーズに絶えず直面しています。 ……言語メカニズムとAPI自体の両方のドラフトを送りますので、コメントをいただけないでしょうか。 F# のライブラリーと、.NETライブラリーに期待される標準の両方にとても精通していますから。

この議論の次回のやり取りは2005年5月19日になされましたが、 その時にF# のオブジェクトプログラミング用構文はほぼ最終的な形をとりました(後から追加した、暗黙的なコンストラクターを除きます)。

type X =
    override x.ToString() = "abc"

    member x.InstanceProperty = "fooproperty"

    member x.MutableInstanceProperty
        with get() = "fooproperty"
        and set(v) = System.Console.WriteLine("mutated!")

    member x.InstanceIndexer
        with get(v) = v+1

    member x.InstanceMethod(s1) = "baz"

    static member StaticProperty = "fooproperty"

    static member MutableStaticProperty
        with get() = "fooproperty"
        and set(v) = System.Console.WriteLine("mutated!")

    static member StaticMethod(s1,s2) = "static method"

この構文では、 “x” は “this” または “self” パラメーターの名前です。 パラメーターにユーザーが明示的に定義する名前を使用するという決定は、部分的にはOCamlシステムで同様の決定がなされたことに影響されましたが、 別の部分では、Javaの内部クラスにおける “this” の解決がややこしいという、学術論文を査読していた時に私が経験した「気持ち悪さ」の感覚によるものでした。 そのような構成体がネストすることは結局必要となりますし、そしてML系言語では普通と考えられるので、明示的な名前を要求するほうが良いでしょう。

振り返ってみると、F# へのオブジェクトプログラミングの追加は、 オブジェクト指向を、ドット表記、クラス、メソッドのオーバーロードなど、約20の個別機能からなる必須要素に「分解」するプロセスでした。 私はのちに、プライベートメールの中でこのリストを次のように体系立てました。

  1. F#にとって許容可能なオブジェクトプログラミング機能:
    1. インスタンスのプロパティとメソッド、および型に導かれる名前解決
    2. 暗黙のコンストラクター
    3. 静的メンバー、つまり型名を修飾子として使うこと
    4. インデクサー記法 arr.[x]
    5. メソッドの名前付き引数とオプション引数
    6. 階層的ではないインターフェース型
    7. オブジェクト式
    8. オブジェクト、レコード、および共用体型の明示的なインターフェイス実装
  2. F#にとって「性能やAPI設計のためにどうしても必要なら仕方ない」オブジェクトプログラミング機能:
    1. ミュータブルなデータ
    2. イベントの定義
    3. 型に対する演算子の定義
    4. 自動プロパティ
    5. IDisposableIEnumerable の実装
    6. 型拡張の節度ある利用
    7. 構造体(性能のため)
    8. デリゲート(相互運用性のため)
    9. 列挙体(相互運用性のため)
    10. メソッドのオーバーロード
    11. 追加のプライマリーコンストラクター(オーバーロードの形をとる)
  3. F#にとって「できれば避けるべき」オブジェクトプログラミング機能:
    1. 実装の継承
    2. Nullと Unchecked.defaultof<_>
  4. まったくサポートされないオブジェクトプログラミング機能:
    1. protected メンバー(実装継承を促進するだけの機能)
    2. アスペクト指向プログラミングに関するすべて

この分解プロセスを通じて、上記の機能は、コアである式言語の本質を維持し、継承よりも委任を強調するようにして、段階的にF# に組み込まれました。 私は最近では、「F# は『オブジェクト』プログラミングを受け入れますが、『オブジェクト指向』プログラミング、特に実装継承は重視しないようにしています」と要約しています[Syme 2018]。 たとえば、 “protected” アクセシビリティ修飾子は、実装継承を助長すると考えられているため、F#では現在でもサポートされていません。

F# 1.0では、式やクラスバインディングやモジュールレベルのバインディングに let mutable x = ... という宣言を書くことができるようになりました。 当初、このような値はクロージャー内からアクセスできなかったので、相互運用コードを書くためだけの機能でした。 その後、F# 3.1では、Matthew Parkinsonが、これらの値を暗黙のうちに参照セルに変換するコンパイラー変換を実装したおかげで、制限が解除されました。

この頃F# は、オブジェクト指向プログラミングを分解した唯一の言語というわけではありませんでした。 そのような言語にはGJやPizza言語などがありましたが、いくつかは、アカデミックなプログラミング言語設計コミュニティによるオブジェクト指向に対する反応を記している節で論じています。 他の例には『Effective Java』[Bloch 2001]があります。この本は継承よりもコンポジションを強調しています。


インデックスへ戻る


  1. 信じられないことに、この機能はいまだにC# に取り込まれていません。 ↩︎