Haskellの型クラスとGoのインターフェースの違いは何ですか?


8

Haskellの型クラスとGoのインターフェースに違いがあるかどうか疑問に思っています。タイプに必要な関数が値に定義されている場合、どちらも関数に基づいてタイプを定義します。つまり、値はタイプに一致します。

違いはありますか、またはこれは同じものの2つの名前だけですか?

回答:


6

2つの概念は非常によく似ています。通常のOOP言語では、各オブジェクトにvtable(またはインターフェースの場合はitable)をアタッチします。

| this
v
+---+---+---+
| V | a | b | the object with fields a, b
+---+---+---+
  |
  v
 +---+---+---+
 | o | p | q | the vtable with method slots o(), p(), q()
 +---+---+---+

これにより、と同様のメソッドを呼び出すことができますthis->vtable.p(this)

Haskellでは、メソッドテーブルは暗黙の隠し引数に似ています。

method :: Class a => a -> a -> Int

C ++関数のようになります

template<typename A>
int method(Class<A>*, A*, A*)

ここで、Class<A>型クラスのインスタンスであるClassタイプのためにA。メソッドは次のように呼び出されます

typeclass_instance->p(value_ptr);

インスタンスは値とは別です。値はまだ実際のタイプを保持しています。型クラスはいくつかのポリモーフィズムを許可しますが、これはポリモーフィズムのサブタイプ化ではありません。これは、を満たす値のリストを作成することを不可能にしますClass。たとえば、とがあるinstance Class Int ...とするとinstance Class String ...、のような[Class]値を持つような異種のリストタイプを作成することはできません[42, "foo"]。(これは、「アプローチ」に効果的に切り替わる「既存のタイプ」拡張を使用する場合に可能です。

Goでは、値は固定された一連のインターフェースを実装していません。そのため、vtableポインターを含めることはできません。代わりに、インターフェイス型へのポインターは、データへの1つのポインターとitableへの別のポインターを含むファットポインターとして実装されます

    `this` fat pointer
    +---+---+
    |   |   |
    +---+---+
 ____/    \_________
v                   v
+---+---+---+       +---+---+
| o | p | q |       | a | b | the data with
+---+---+---+       +---+---+ fields a, b
itable with method
slots o(), p(), q()

this.itable->p(this.data_ptr)

itableは、通常の値からインターフェイス型にキャストするときに、データと結合してファットポインターになります。インターフェース型を取得すると、データの実際の型は無関係になります。実際、メソッドを通過したり、インターフェースをダウンキャストしたりせずにフィールドに直接アクセスすることはできません(失敗する可能性があります)。

インターフェースのディスパッチへのGoのアプローチにはコストがかかります。各ポリモーフィックポインターは通常のポインターの2倍の大きさです。また、あるインターフェースから別のインターフェースへのキャストには、メソッドポインターを新しいvtableにコピーすることが含まれます。しかしitableを作成したら、これにより多くのインターフェースへのメソッド呼び出しを安価にディスパッチできます。これは従来のOOP言語が抱えている問題です。ここで、mはターゲットインターフェイスのメソッドの数で、bは基本クラスの数です。

  • C ++は、オブジェクトのスライスを行うか、キャスト時に仮想継承ポインターを追跡する必要がありますが、単純なvtableアクセスを持っています。O(1)またはO(b)アップキャストのコスト、ただしO(1)メソッドのディスパッチ。
  • Java Hotspot VMはアップキャスト時に何もする必要はありませんが、インターフェースメソッドの検索時に、そのクラスによって実装されているすべてのitablesを線形検索します。O(1)アップキャスト、ただしO(b)メソッドディスパッチ。
  • Pythonはアップキャスト時に何もする必要はありませんが、C3線形化基本クラスリストによる線形検索を使用します。O(1)アップキャスト、ただしO(b²)メソッドディスパッチ?C3のアルゴリズムの複雑さは何かわかりません。
  • .NET CLRはHotspotと同様のアプローチを使用しますが、メモリ使用量を最適化するために、別のレベルの間接参照を追加します。O(1)アップキャスト、ただしO(b)メソッドディスパッチ。

メソッドのディスパッチは通常キャッシュされるため、メソッドディスパッチの一般的な複雑さははるかに優れていますが、最悪の場合の複雑さは非常に恐ろしいものです。

それに比べて、GoにはO(1)またはO(m)のアップキャストとO(1)メソッドのディスパッチがあります。Haskellにはアップキャスト(型クラスでの型の制約はコンパイル時の効果です)とO(1)メソッドのディスパッチはありません。


をありがとう[42, "foo"]。鮮やかな例です。
16

2
この回答はよく書かれていて有用な情報が含まれていますが、コンパイルされたコードの実装に焦点を合わせると、インターフェイスと型クラスの類似性が大幅に誇張されると思います。型クラス(およびより一般的にはHaskell型システム)では、興味深いもののほとんどがコンパイル中に発生し、最終的なマシンコードには反映されません。
KA Buhr 2017

7

いくつかの違いがあります

  1. Haskellの型クラスは主格的に型付けされています。これMaybeはであると宣言する必要がありMonadます。Goインターフェースは構造的に型付けされています。circle宣言した場合area() float64もそうである場合square、両方ともshape自動的にインターフェースの下に置かれます。
  2. Haskell(GHC拡張あり)には、Multi Parameter Type Classesと(Maybe a例のように)より高い種類の型の型クラスがあります。Goにはこれらに相当するものはありません。
  3. Haskellでは、型クラスはバインドされたポリモーフィズムで消費され、Goでは表現できない制約が与えられます。たとえば+ :: Num a => a -> a -> a、浮動小数点と四元数を追加しようとしないことを保証するは、Goでは表現できません。

1.は本当に違いますか、それとも砂糖が不足しているだけですか?
16

1
Goインターフェイスは値のプロトコルを定義し、Haskellタイプクラスはタイプのプロトコルを定義します。これもかなり大きな違いです。(それが、結局「型クラス」と呼ばれる理由です。値を分類するOOクラス(またはGoのインターフェイス)とは異なり、型を分類します。)
JörgW Mittag

1
@ceving、それは間違いなく通常の意味では砂糖ではありません:Haskellが型クラスの暗黙的なScalaのようなものにジャンプした場合、既存のコードの多くが破損します。
2016

@JörgWMittag私は同意します。私はあなたの答えが好きでした。ユーザーの観点からもっと多くの違いを得ようとしていました。
2016

@walpenなぜこれがコードを壊すのですか?Haskellの型システムの厳密さを考えると、このようなコードはどのように存在するのでしょうか。
16

4

それらは完全に異なります。Goインターフェースは値のプロトコルを定義し、Haskellタイプクラスはタイプのプロトコルを定義します。(そのため、結局、それらは「型クラス」と呼ばれます。値を分類するOOクラス(またはGoのインターフェース)とは異なり、型を分類します。)

Goインターフェースは、古い構造的な型付けを退屈にするだけで、それ以上のものはありません。


1
説明できますか?多分屈辱的なトーンがなくても。私が読んだことによると、型クラスは、Goのインターフェイスと同じである演算子のオーバーロードに相当するアドホックなポリモーフィズムです。
16

ハスケルからチュートリアル「インターフェース宣言と同様に、Haskellのクラス宣言は、オブジェクトを使用するためのプロトコルを定義」:
ceving

2
同じチュートリアル(太字強調)から:「型クラス[…]どのがどのクラスのインスタンスであるかを宣言できます」Goインターフェースのインスタンスはであり、Haskell型クラスのインスタンスはです。値と型は2つの完全に異なる世界にあります(少なくともHaskellやGoなどの言語では、Agda、Guru、Epigram、Idris、Isabelle、Coqなどの依存型の言語は別の問題です)。
イェルクWミッターク

答えは洞察に富んでいるので投票するが、もっと詳細に役立つと思います。そして、構造型付けについて退屈なのは何ですか?それはかなりまれであり、私の意見では祝う価値があります。
Max Heiber
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.