Was ist der Unterschied zwischen Merkmalen in Rust und Typklassen in Haskell?

157

Merkmale in Rust scheinen zumindest oberflächlich Typklassen in Haskell ähnlich zu sein , aber ich habe Leute schreiben sehen, dass es einige Unterschiede zwischen ihnen gibt. Ich habe mich genau gefragt, was diese Unterschiede sind.

LogicChains
quelle
8
Ich weiß nicht viel über Rust. Häufige Stolpersteine ​​für ähnliche Technologien in anderen Sprachen sind jedoch höhere Arten (z. B. können Merkmale über parametrisierte Typen reichen, aber nicht deren Parameter?) Und Polymorphismus vom Rückgabetyp (z. B. kann ein Merkmalstyp im Ergebnis einer Funktion auftreten, jedoch nirgendwo in den Argumenten?). Ein Beispiel für das erstere in Haskell ist class Functor f where fmap :: (a -> b) -> (f a -> f b); Ein Beispiel für Letzteres ist class Bounded a where maxBound :: a.
Daniel Wagner
4
GHC unterstützt auch Multiparameter-Typklassen (dh Merkmale mit mehreren Typen) und funktionale Abhängigkeiten, obwohl dies nicht Teil der offiziellen Haskell-Spezifikation ist. Nach der in Ihrem Link vorgeschlagenen Rust-Syntax zu urteilen, kann sie nur Merkmale unterstützen, die sich jeweils über einen Typ erstrecken, obwohl dieses Urteil wiederum nicht auf tiefer Erfahrung basiert.
Daniel Wagner
4
@DanielWagner Es gibt einen Polymorphismus vom Rückgabetyp (z. B. std::default), und Multiparameter-Merkmale funktionieren (einschließlich eines Analogons funktionaler Abhängigkeiten), obwohl AFAIK den ersten privilegierten Parameter umgehen muss. Kein HKT jedoch. Sie stehen auf der Wunschliste der fernen Zukunft, aber noch nicht am Horizont.
4
Ein weiterer Unterschied ist die Behandlung von Waisenfällen. Rust versucht, strengere Kohärenzregeln zu haben, wo ein neues Impl für ein Merkmal geschrieben werden kann. Siehe diese Diskussion für weitere Details (insbesondere hier )
Paolo Falabella
1
Rust unterstützt jetzt zugehörige Typen und Gleichheitsbeschränkungen , obwohl diese nicht so leistungsfähig sind wie die Typfamilien von Haskell. Es hat auch existenzielle Typen über Merkmalsobjekte .
Lambda Fairy

Antworten:

60

Im Grunde gibt es keinen großen Unterschied, aber sie sind immer noch da.

Haskell beschreibt Funktionen oder Werte, die in einer Typklasse definiert sind, als 'Methoden', genauso wie Merkmale OOP-Methoden in den Objekten beschreiben, die sie einschließen. Haskell geht jedoch anders mit diesen um und behandelt sie als einzelne Werte, anstatt sie an ein Objekt zu heften, wie dies OOP veranlassen würde. Hier geht es um den offensichtlichsten Unterschied auf Oberflächenebene.

Das einzige, was Rust für eine Weile nicht tun konnte , waren typisierte Merkmale höherer Ordnung , wie die berüchtigten Functorund MonadTypklassen.

Dies bedeutet, dass Rostmerkmale nur beschreiben können, was oft als "konkreter Typ" bezeichnet wird, mit anderen Worten, einer ohne generisches Argument. Haskell konnte von Anfang an Typklassen höherer Ordnung erstellen, die Typen verwenden, die denen ähnlicher Funktionen höherer Ordnung ähneln: Verwenden einer, um eine andere zu beschreiben. Für eine gewisse Zeit war dies in Rust nicht möglich, aber seitdem zugehörige Elemente implementiert wurden, sind solche Merkmale alltäglich und idiomatisch geworden.

Wenn wir also Erweiterungen ignorieren, sind sie nicht genau gleich, aber jede kann sich annähern, was die andere tun kann.

Wie in den Kommentaren erwähnt, ist es auch erwähnenswert, dass GHC (Haskells Hauptcompiler) weitere Optionen für Typklassen unterstützt, einschließlich Typklassen mit mehreren Parametern (dh vielen beteiligten Typen) und funktionale Abhängigkeiten , eine schöne Option, die Berechnungen auf Typebene ermöglicht und führt zu Typfamilien . Meines Wissens hat Rust weder funDeps noch Typfamilien, auch wenn dies in Zukunft der Fall sein könnte. †

Alles in allem weisen Merkmale und Typklassen grundlegende Unterschiede auf, die aufgrund ihrer Interaktion dazu führen, dass sie sich verhalten und am Ende ziemlich ähnlich erscheinen.


† Einen schönen Artikel über Haskells Typklassen (einschließlich höher typisierter Klassen) finden Sie hier , und das Kapitel Rust by Example über Merkmale finden Sie hier

AJFarmar
quelle
1
Rust hat immer noch keine höherwertigen Typen. "Berüchtigt" muss begründet werden. Funktoren sind unglaublich allgegenwärtig und als Konzept nützlich. Typfamilien sind dieselben wie zugeordnete Typen. Funktionale Abhängigkeiten sind mit zugehörigen Typen (einschließlich in Haskell) im Wesentlichen redundant. Dem Ding fehlt Rust wrt. fundeps ist Injektionsanmerkungen. Sie haben es rückwärts, Rusts Eigenschaften und Haskells Typklassen sind an der Oberfläche unterschiedlich, aber viele Unterschiede verschwinden, wenn Sie nach unten schauen. Die verbleibenden Unterschiede sind hauptsächlich auf die verschiedenen Bereiche zurückzuführen, in denen die Sprachen tätig sind.
Centril
Zugehörige Elemente werden heute unter vielen Umständen als idomatisch angesehen, oder?
Vaelus
@Vaelus Sie haben Recht - Diese Antwort sollte ein wenig aktualisiert werden. Jetzt bearbeiten.
AJFarmar
19

Ich denke, die aktuellen Antworten übersehen die grundlegendsten Unterschiede zwischen Rust-Merkmalen und Haskell-Typklassen. Diese Unterschiede hängen damit zusammen, wie Merkmale mit objektorientierten Sprachkonstrukten zusammenhängen. Informationen hierzu finden Sie im Rust-Buch .

  1. Eine Merkmalsdeklaration erstellt einen Merkmalstyp . Dies bedeutet, dass Sie Variablen eines solchen Typs (oder vielmehr Referenzen des Typs) deklarieren können. Sie können Merkmalstypen auch als Parameter für Funktions-, Strukturfelder und Typparameterinstanziierungen verwenden.

    Eine Merkmalsreferenzvariable kann zur Laufzeit Objekte unterschiedlichen Typs enthalten, solange der Laufzeittyp des referenzierten Objekts das Merkmal implementiert.

    // 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();
    

    So funktionieren Typklassen nicht. Typklassen erstellen keine Typen , daher können Sie keine Variablen mit dem Klassennamen deklarieren. Typklassen dienen als Grenzen für Typparameter, aber die Typparameter müssen mit einem konkreten Typ instanziiert werden, nicht mit der Typklasse selbst.

    Sie können keine Liste verschiedener Dinge unterschiedlichen Typs haben, die dieselbe Typklasse implementieren. (Stattdessen werden in Haskell existenzielle Typen verwendet, um etwas Ähnliches auszudrücken.) Hinweis 1

  2. Merkmalsmethoden können dynamisch versendet werden . Dies hängt stark mit den Dingen zusammen, die im obigen Abschnitt beschrieben werden.

    Dynamischer Versand bedeutet, dass der Laufzeittyp des Objekts, auf das ein Referenzpunkt verweist, verwendet wird, um zu bestimmen, welche Methode über die Referenz aufgerufen wird.

    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());
    

    Auch hierfür werden in Haskell existenzielle Typen verwendet.

Abschließend

Es scheint mir, dass Merkmale in vielerlei Hinsicht das gleiche Konzept wie Typklassen sind. Darüber hinaus verfügen sie über die Funktionalität objektorientierter Schnittstellen.

Andererseits sind die Typklassen von Haskell weiter fortgeschritten. Haskell hat zum Beispiel höherwertige Typen und Erweiterungen wie Typklassen mit mehreren Parametern.


Hinweis 1 : Neuere Versionen von Rust verfügen über ein Update, um die Verwendung von Merkmalsnamen als Typen und die Verwendung von Merkmalsnamen als Grenzen zu unterscheiden. In einem Merkmalstyp wird dem Namen das dynSchlüsselwort vorangestellt . Weitere Informationen finden Sie beispielsweise in dieser Antwort .

Lii
quelle
2
"Typklassen schaffen keine Typen" - Ich denke, es ist am besten, sie dyn Traitals eine Form existenzieller Typisierung zu verstehen, da sie sich auf Merkmale / Typklassen beziehen. Wir können dyneinen Operator an Grenzen betrachten, der sie auf Typen projiziert, d dyn : List Bound -> Type. H. Wenn wir diese Idee zu Haskell bringen und in Bezug auf "Sie können also keine Variablen mit dem Klassennamen deklarieren.", Können wir dies indirekt in Haskell tun : data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t. Nachdem wir dies definiert haben, können wir mit arbeiten [D True, D "abc", D 42] :: [D Show].
Centril
8

Rusts "Merkmale" sind analog zu Haskells Typklassen.

Der Hauptunterschied zu Haskell besteht darin, dass Merkmale nur für Ausdrücke mit Punktnotation eingreifen, dh für die Form a.foo (b).

Haskell-Typklassen erstrecken sich auf Typen höherer Ordnung. Rostmerkmale unterstützen nur keine Typen höherer Ordnung, da sie in der gesamten Sprache fehlen, dh es ist kein philosophischer Unterschied zwischen Merkmalen und Typklassen

Anuj Gupta
quelle
1
Merkmale in Rust "greifen nicht nur für Ausdrücke mit Punktnotation ein". Betrachten Sie beispielsweise das DefaultMerkmal, das keine Methoden enthält, sondern nur Funktionen, die nicht mit Methoden verknüpft sind.
Centril