回答:
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は基本クラスの数です。
メソッドのディスパッチは通常キャッシュされるため、メソッドディスパッチの一般的な複雑さははるかに優れていますが、最悪の場合の複雑さは非常に恐ろしいものです。
それに比べて、GoにはO(1)またはO(m)のアップキャストとO(1)メソッドのディスパッチがあります。Haskellにはアップキャスト(型クラスでの型の制約はコンパイル時の効果です)とO(1)メソッドのディスパッチはありません。
いくつかの違いがあります
Maybe
はであると宣言する必要がありMonad
ます。Goインターフェースは構造的に型付けされています。circle
宣言した場合area() float64
もそうである場合square
、両方ともshape
自動的にインターフェースの下に置かれます。Maybe a
例のように)より高い種類の型の型クラスがあります。Goにはこれらに相当するものはありません。+ :: Num a => a -> a -> a
、浮動小数点と四元数を追加しようとしないことを保証するは、Goでは表現できません。それらは完全に異なります。Goインターフェースは値のプロトコルを定義し、Haskellタイプクラスはタイプのプロトコルを定義します。(そのため、結局、それらは「型クラス」と呼ばれます。値を分類するOOクラス(またはGoのインターフェース)とは異なり、型を分類します。)
Goインターフェースは、古い構造的な型付けを退屈にするだけで、それ以上のものはありません。
[42, "foo"]
。鮮やかな例です。