F# は OOP を使用した ML です。Haskell の一般化された代数データ型と型クラスに最も近いものは何ですか?
2 に答える
答えは、解決しようとしている問題によって異なります。F# には型クラスと GADT がないため、直接のマッピングはありません。ただし、F# には、GADT と型クラスを使用して Haskell で通常解決する問題を解決するために使用するさまざまなメカニズムがあります。
オブジェクト構造を表現し、異なる動作を持つ新しい具体的な実装を追加できるようにしたい場合は、多くの場合、標準の OO とインターフェイスを使用できます。
一般的な数値コードを記述したい場合は、静的メンバー制約 (ここに例を示します) を使用できます。これは、おそらく技術的に型クラスに最も近いメカニズムです。
より高度な汎用コード (ユニバーサル プリンターやパーサーなど) を記述したい場合は、多くの場合、強力な F# ランタイム リフレクション機能を使用できます。
一連の関数 (コードに必要なさまざまなサブ操作を実行する関数) によってコードをパラメーター化する必要がある場合は、@pad が示すようにインターフェイスの実装を渡すことができます。
F# で Haskell 型クラスをエミュレートする方法もありますが、F# プログラミング スタイルは多くの点で Haskell スタイルと異なるため、これは通常、慣用的な F# ソリューションではありません。ただし、これのかなり標準的な使用方法の 1 つは、オーバーロードされた演算子を定義することです (この SO の回答を参照してください)。
メタレベルでは、別の言語の機能 X に相当するものは何かを尋ねると、混乱した議論につながることがよくあります。なぜなら、ある言語では問題 A、B、C を解決するために X が使用され、別の言語では解決するさまざまな機能が提供される可能性があるためです。同じ問題があります (または、一部の問題がまったく存在しない場合もあります)。
F# では、これらの目的でインターフェイスと継承をよく使用します。
例として、インターフェイスとオブジェクト式を使用した単純な型クラスを次に示します。
/// Typeclass
type MathOps<'T> =
abstract member Add : 'T -> 'T -> 'T
abstract member Mul : 'T -> 'T -> 'T
/// An instance for int
let mathInt =
{ new MathOps<int> with
member __.Add x y = x + y
member __.Mul x y = x * y }
/// An instance for float
let mathFloat =
{ new MathOps<float> with
member __.Add x y = x + y
member __.Mul x y = x * y }
let XtimesYplusZ (ops: MathOps<'T>) x y z =
ops.Add (ops.Mul x y) z
printfn "%d" (XtimesYplusZ mathInt 3 4 1)
printfn "%f" (XtimesYplusZ mathFloat 3.0 4.0 1.0)
見た目はあまり美しくないかもしれませんが、F# らしい方法です。操作の辞書を使用する、より Haskell に似たソリューションについては、この素敵な答えを見ることができます。