Rustの特性とHaskellの型クラスの違いは何ですか?


157

Rustの特性は、少なくとも表面的にはHaskellのタイプクラスに似ているように見えますが、それらの間にはいくつかの違いがあると人々が書いているのを見てきました。私はこれらの違いが何であるかを正確に疑問に思っていました。


8
Rustについてはあまり知りません。しかし、他の言語における同様のテクノロジーの一般的な障害は、より高い種類(たとえば、特性はパラメーター化された型の範囲にあり、パラメーターにはないのか?)と戻り型の多型(たとえば、特性の型は関数の結果に現れることができますが、どこにもありません)引数で?)Haskellにおける前者の例は次のとおりですclass Functor f where fmap :: (a -> b) -> (f a -> f b)。後者の例はclass Bounded a where maxBound :: aです。
Daniel Wagner

4
GHCは、マルチパラメーター型クラス(つまり、いくつかの型を含む特性)と機能の依存関係もサポートしていますが、これは公式のHaskell仕様の一部ではありません。リンクで提案されているRust構文から判断すると、一度に1つのタイプに及ぶ特性しかサポートできませんが、その判断は深い経験に基づいていません。
Daniel Wagner

4
@DanielWagner戻り型のポリモーフィズムが存在し(例:)std::default、マルチパラメーター特性の一種の作業(機能的な依存関係の類似物を含む)ですが、特権を持つ最初のパラメーターを回避する必要があります。ただし、HKTはありません。彼らは遠い将来の希望リストに載っていますが、まだ地平線上にはありません。

4
別の違いは、孤立したインスタンスの扱いです。Rustは、特性の新しい実装をどこに書き込むことができるかについて、より厳密な一貫性のあるルールを設定しようとします。詳細については、このディスカッションを参照してください(特にここ
Paolo Falabella、2015年

1
Rustは、Haskellの型ファミリーほど強力ではありませんが、関連する型と等式制約をサポートしています。また、特性オブジェクトを介した実存型もあります
ラムダフェアリー

回答:


61

基本的なレベルでは、それほど大きな違いはありませんが、まだ残っています。

Haskellは、型クラスで定義された関数または値を「メソッド」として記述します。特性が、それらが囲むオブジェクトのOOPメソッドを記述します。ただし、Haskellはこれらを異なる方法で処理し、OOPのようにオブジェクトに固定するのではなく、個別の値として扱います。これは、存在する最も明白な表面レベルの違いについてです。

Rustがしばらくできなかったことの1つは、悪名高いクラスや型クラスなどの高次の型付き特性ですFunctorMonad

つまり、Rustの特性は、「コンクリートタイプ」と呼ばれるもの、つまり一般的な引数を持たないもののみを記述できるということです。Haskellは最初から、高次関数が他の関数を使用する方法と同様の型を使用する高次型クラスを作成できます。しばらくの間、Rustではこれは不可能でしたが、関連するアイテムが実装されて以来、そのような特性は一般的で慣用的になっています。

したがって、拡張機能を無視すると、それらはまったく同じではありませんが、それぞれが他の機能と同じように機能します。

コメントで述べたように、GHC(Haskellの主要コンパイラー)が、マルチパラメーター(つまり、多くの型が関与する)型クラスを含む型クラスのさらなるオプション、および関数型の依存関係(型レベルの計算を可能にする素敵なオプション)をサポートすることも言及できます。 、そしてタイプファミリーにつながります。私の知る限り、RustにはfunDepsもタイプファミリーもありませんが、将来的には可能性があります。†

全体として、トレイトとタイプクラスには根本的な違いがあります。それは、それらが相互作用する方法により、それらを機能させ、最終的には非常に似ているように見えます。


†Haskellの型クラス(より高い型のクラスを含む)に関する素晴らしい記事はここにあり、特性に関するRust by Exampleの章はここにあります。


1
Rustにはまだ、より高い種類の型はありません。「悪名高い」には正当化が必要です。Functorは非常に普及していて、概念としては便利です。タイプファミリーは、関連するタイプと同じです。機能の依存関係は、関連する型(Haskellを含む)と本質的に重複しています。Rustにはwrtがありません。Fundepsは単射性注釈です。Rustの特性とHaskellの型クラスは表面的には異なりますが、下を見ると多くの違いがなくなります。残っている違いは、言語が動作するさまざまなドメインに固有のものです
。– Centril

関連アイテムは今、多くの状況でイドマティックと見なされていますよね?
Vaelus

@Vaelusそうです—この回答は少し更新する必要があります。編集しています。
AJFarmar

19

現在の答えは、RustトレイトとHaskell型クラスの最も基本的な違いを見落としていると思います。これらの違いは、特性がオブジェクト指向言語の構成要素に関連する方法に関係しています。これについては、Rustブックを参照してください。

  1. 特性宣言は、特性タイプを作成します。つまり、そのような型の変数(または、型の参照)を宣言できます。関数、構造体フィールド、および型パラメーターのインスタンス化のパラメーターとして特性型を使用することもできます。

    参照されるオブジェクトのランタイムタイプが特性を実装している限り、実行時に特性参照変数にさまざまなタイプのオブジェクトを含めることができます。

    // The shape variable might contain a Square or a Circle, 
    // we don't know until runtime
    let shape: &Shape = get_unknown_shape();
    
    // Might contain different kinds of shapes at the same time
    let shapes: Vec<&Shape> = get_shapes();

    これは、型クラスが機能する方法ではありません。型クラスは型を作成しないため、クラス名で変数を宣言することはできません。型クラスは型パラメーターの境界として機能しますが、型パラメーターは型クラス自体ではなく具象型でインスタンス化する必要があります。

    同じ型クラスを実装する異なる型の異なるもののリストを持つことはできません。(代わりに、存在型はHaskellでは同様のものを表すために使用されます。)注1

  2. トレイトメソッドは動的にディスパッチできます。これは、前のセクションで説明したことと強く関連しています。

    動的ディスパッチとは、参照が指すオブジェクトの実行時のタイプを使用して、参照を通じて呼び出されるメソッドを決定することを意味します。

    let shape: &Shape = get_unknown_shape();
    
    // This calls a method, which might be Square.area or
    // Circle.area depending on the runtime type of shape
    print!("Area: {}", shape.area());

    繰り返しになりますが、Haskellではこれに存在型が使用されています。

結論として

トレイトは多くの点で型クラスと同じ概念であるように思えます。さらに、オブジェクト指向インターフェースの機能を備えています。

一方、Haskellの型クラスはより高度です。Haskellには、たとえば、より高い種類の型や、マルチパラメータ型クラスのような拡張機能があります。


注1:Rustの最近のバージョンでは、タイプとしての特性名の使用と境界としての特性名の使用を区別するための更新が行われています。特性タイプでは、名前の前にdynキーワードが付きます。詳細については、たとえばこの回答を参照してください。


2
「型クラスは型を作成しない」- dyn Trait特性/型クラスに関連するため、存在型の形式として理解するのが最善だと思います。dynそれらを型に射影する境界の演算子、つまりを考えることができdyn : List Bound -> Typeます。このアイデアをHaskellに持ち帰り、「クラス名で変数を宣言できないようにする」ことに関して、Haskellで間接的にこれを行うことができますdata Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t。これを定義したら、で作業でき[D True, D "abc", D 42] :: [D Show]ます。
Centril 2019

8

Rustの「特性」はHaskellの型クラスに類似しています。

Haskellとの主な違いは、トレイトはドット表記(つまりa.foo(b)の形式)を持つ式にのみ介入することです。

Haskell型クラスは、より高次の型に拡張されます。Rustトレイトは、言語全体から欠落しているため、高次の型のみをサポートしません。つまり、トレイトと型クラスの哲学的な違いではありません。


1
Rustの特性は、「ドット表記の式に介入するだけ」ではありません。たとえば、Defaultメソッドを持たず、メソッドに関連付けられていない関数のみを含むトレイトについて考えてみます。
Centril 2019
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.