Während ich Haskell lerne, habe ich seine Typklasse bemerkt , die eine großartige Erfindung sein soll, die von Haskell stammt.
Auf der Wikipedia-Seite zur Typklasse :
Der Programmierer definiert eine Typklasse, indem er eine Reihe von Funktions- oder Konstantennamen zusammen mit ihren jeweiligen Typen angibt, die für jeden Typ vorhanden sein müssen, der zur Klasse gehört.
Das scheint mir ziemlich nah an Javas Schnittstelle zu sein (zitiert die Java-Seite von Wikipedia ):
Eine Schnittstelle in der Programmiersprache Java ist ein abstrakter Typ, mit dem eine Schnittstelle (im allgemeinen Sinne des Begriffs) angegeben wird, die Klassen implementieren müssen.
Diese beiden sehen sich ziemlich ähnlich: Typklassen beschränken das Verhalten eines Typs, während Schnittstellen das Verhalten einer Klasse einschränken.
Ich frage mich, was die Unterschiede und Ähnlichkeiten zwischen der Typklasse in Haskell und der Schnittstelle in Java sind oder ob sie sich grundlegend unterscheiden.
EDIT: Mir ist aufgefallen, dass sogar haskell.org zugibt, dass sie ähnlich sind . Wenn sie sich so ähnlich sind (oder?), Warum wird dann die Typklasse mit einem solchen Hype behandelt?
MEHR BEARBEITEN: Wow, so viele tolle Antworten! Ich denke, ich muss die Community entscheiden lassen, welches das beste ist. Beim Lesen der Antworten scheinen alle nur zu sagen, dass "es viele Dinge gibt, die Typklassen tun können, während die Benutzeroberfläche nicht mit Generika umgehen kann oder muss" . Ich kann nicht anders, als mich zu fragen, ob Schnittstellen etwas tun können, während Typklassen dies nicht können. Außerdem habe ich festgestellt, dass Wikipedia behauptet, dass die Typklasse ursprünglich in der Veröffentlichung von 1989 * "Wie man Ad-hoc-Polymorphismus weniger ad hoc macht" erfunden wurde, während Haskell noch in der Wiege steckt, während das Java-Projekt 1991 gestartet und 1995 erstmals veröffentlicht wurde Also. vielleicht statt typeclass ähnlich wie Schnittstellen zu sein, seine umgekehrt, dass Schnittstellen von typeclass beeinflusst wurde?Gibt es Dokumente / Papiere, die dies unterstützen oder widerlegen? Vielen Dank für alle Antworten, sie sind alle sehr aufschlussreich!
Danke für alle Eingaben!
Antworten:
Ich würde sagen, dass eine Schnittstelle wie eine Typklasse ist,
SomeInterface t
in der alle Werte den Typ habent -> whatever
(wowhatever
nicht enthaltent
). Dies liegt daran, dass bei der Art der Vererbungsbeziehung in Java und ähnlichen Sprachen die aufgerufene Methode von der Art des Objekts abhängt, für das sie aufgerufen werden, und von nichts anderem.Das bedeutet, dass es sehr schwierig ist, Dinge wie
add :: t -> t -> t
mit einer Schnittstelle zu erstellen, bei der sie für mehr als einen Parameter polymorph ist, da die Schnittstelle nicht angeben kann, dass der Argumenttyp und der Rückgabetyp der Methode vom Typ sind das Objekt, auf das es aufgerufen wird (dh der Typ "Selbst"). Mit Generics gibt es einige Möglichkeiten, dies zu fälschen, indem Sie eine Schnittstelle mit generischen Parametern erstellen, von denen erwartet wird, dass sie vom selben Typ wie das Objekt selbst sind, wie esComparable<T>
funktioniert, wo Sie sie verwenden sollen,Foo implements Comparable<Foo>
damit diecompareTo(T otherobject)
Art von Typ hatt -> t -> Ordering
. Dies erfordert jedoch weiterhin, dass der Programmierer diese Regel befolgt, und verursacht auch Kopfschmerzen, wenn Benutzer eine Funktion erstellen möchten, die diese Schnittstelle verwendet, müssen sie rekursive generische Typparameter haben.Außerdem werden Sie keine Dinge haben,
empty :: t
weil Sie hier keine Funktion aufrufen, es ist also keine Methode.quelle
Was zwischen Schnittstellen und Typklassen ähnlich ist, ist, dass sie eine Reihe verwandter Operationen benennen und beschreiben. Die Operationen selbst werden über ihre Namen, Ein- und Ausgänge beschrieben. Ebenso kann es viele Implementierungen dieser Operationen geben, die sich wahrscheinlich in ihrer Implementierung unterscheiden.
Hier sind einige bemerkenswerte Unterschiede:
Im Allgemeinen denke ich, dass es fair ist zu sagen, dass Typklassen leistungsfähiger und flexibler sind als Schnittstellen. Wie würden Sie eine Schnittstelle zum Konvertieren einer Zeichenfolge in einen Wert oder eine Instanz des Implementierungstyps definieren? Es ist sicherlich nicht unmöglich, aber das Ergebnis wäre nicht intuitiv oder elegant. Haben Sie sich jemals gewünscht, eine Schnittstelle für einen Typ in einer kompilierten Bibliothek implementieren zu können? Beides ist mit Typklassen leicht zu erreichen.
quelle
Typklassen wurden als strukturierte Ausdrucksweise für "Ad-hoc-Polymorphismus" erstellt, der im Grunde der Fachbegriff für überladene Funktionen ist . Eine Typklassendefinition sieht ungefähr so aus:
Dies bedeutet, dass beim Anwenden der Funktion
foo
auf einige Argumente eines Typs, der zur Klasse gehörtFoobar
, einefoo
für diesen Typ spezifische Implementierung nachgeschlagen und verwendet wird. Dies ist der Situation mit Operatorüberladung in Sprachen wie C ++ / C # sehr ähnlich, außer dass sie flexibler und allgemeiner ist.Schnittstellen dienen in OO-Sprachen einem ähnlichen Zweck, aber das zugrunde liegende Konzept ist etwas anders. OO-Sprachen verfügen über eine integrierte Vorstellung von Typhierarchien, die Haskell einfach nicht hat, was die Sache in gewisser Weise kompliziert, da Schnittstellen sowohl eine Überladung durch Subtypisierung beinhalten können (dh Methoden auf geeigneten Instanzen aufrufen, Subtypen, die Schnittstellen implementieren, die ihre Supertypen tun). und durch flachen typbasierten Versand (da zwei Klassen, die eine Schnittstelle implementieren, möglicherweise keine gemeinsame Oberklasse haben, die sie auch implementiert). Angesichts der enormen zusätzlichen Komplexität, die durch die Subtypisierung entsteht, empfehle ich, Typklassen als eine verbesserte Version überladener Funktionen in einer Nicht-OO-Sprache zu betrachten.
Erwähnenswert ist auch, dass Typklassen wesentlich flexiblere Versandmöglichkeiten haben - Schnittstellen gelten im Allgemeinen nur für die einzelne Klasse, die sie implementiert, während Typklassen für einen Typ definiert sind, der an einer beliebigen Stelle in der Signatur der Klassenfunktionen erscheinen kann. Das Äquivalent dazu in OO-Schnittstellen wäre, dass die Schnittstelle Möglichkeiten definiert, ein Objekt dieser Klasse an andere Klassen zu übergeben, statische Methoden und Konstruktoren zu definieren, die eine Implementierung basierend auf dem im Aufrufkontext erforderlichen Rückgabetyp auswählen und Methoden definieren, die Nehmen Sie Argumente des gleichen Typs wie die Klasse, die die Schnittstelle implementiert, und verschiedene andere Dinge, die überhaupt nicht wirklich übersetzt werden.
Kurz gesagt: Sie dienen ähnlichen Zwecken, aber ihre Arbeitsweise ist etwas anders, und Typklassen sind sowohl wesentlich aussagekräftiger als auch in einigen Fällen einfacher zu verwenden, da an festen Typen gearbeitet wird und nicht an Teilen einer Vererbungshierarchie.
quelle
Ich habe die obigen Antworten gelesen. Ich habe das Gefühl, etwas klarer antworten zu können:
Eine Haskell "Typklasse" und eine Java / C # "Schnittstelle" oder ein Scala "Merkmal" sind grundsätzlich analog. Es gibt keine konzeptionelle Unterscheidung zwischen ihnen, aber es gibt Implementierungsunterschiede:
quelle
In Master Minds of Programming gibt es ein Interview über Haskell mit Phil Wadler, dem Erfinder der Typklassen, der die Ähnlichkeiten zwischen Schnittstellen in Java und Typklassen in Haskell erklärt:
Typklassen beziehen sich also auf Schnittstellen, aber die tatsächliche Entsprechung wäre eine statische Methode, die mit einem Typ wie oben parametrisiert wird.
quelle
Sehen Sie sich Phillip Wadlers Vortrag über Glauben, Evolution und Programmiersprachen an . Wadler arbeitete an Haskell und leistete einen wichtigen Beitrag zu Java Generics.
quelle
Lesen Sie Softwareerweiterung und Integration in Typklassen. Hier finden Sie Beispiele dafür, wie Typklassen eine Reihe von Problemen lösen können, die Schnittstellen nicht können.
Beispiele in der Arbeit aufgeführt sind:
quelle
Ich kann nicht mit dem "Hype" -Niveau sprechen, wenn es so gut scheint. Aber ja, Typklassen sind in vielerlei Hinsicht ähnlich. Ein Unterschied, den ich mir vorstellen kann, ist, dass Sie mit Haskell Verhalten für einige Operationen der Typklasse bereitstellen können :
Dies zeigt, dass es zwei Operationen gibt, gleich
(==)
und ungleich(/=)
, für Dinge, die Instanzen derEq
Typklasse sind. Die ungleiche Operation wird jedoch als gleich definiert (sodass Sie nur eine angeben müssen) und umgekehrt.In wahrscheinlich nicht legalem Java wäre das so etwas wie:
und die Art und Weise, wie es funktionieren würde, ist, dass Sie nur eine dieser Methoden bereitstellen müssten, um die Schnittstelle zu implementieren. Ich würde also sagen, dass die Fähigkeit, eine Art Teilimplementierung des gewünschten Verhaltens auf Schnittstellenebene bereitzustellen , einen Unterschied darstellt.
quelle
Sie sind ähnlich (lesen Sie: haben eine ähnliche Verwendung) und werden wahrscheinlich ähnlich implementiert: Polymorphe Funktionen in Haskell nehmen unter der Haube eine 'vtable' auf, in der die mit der Typklasse verbundenen Funktionen aufgelistet sind.
Diese Tabelle kann häufig zur Kompilierungszeit abgeleitet werden. Dies trifft in Java wahrscheinlich weniger zu.
Dies ist jedoch eine Tabelle mit Funktionen , keine Methoden . Methoden sind an ein Objekt gebunden, Haskell-Typklassen nicht.
Sehen Sie sie eher wie Javas Generika.
quelle
Wie Daniel sagt, werden Schnittstellenimplementierungen definiert separat Erklärungen von Daten. Und wie andere bereits betont haben, gibt es eine einfache Möglichkeit, Operationen zu definieren, die denselben freien Typ an mehr als einer Stelle verwenden. Es ist also einfach
Num
als Typklasse zu definieren . So erhalten wir in Haskell die syntaktischen Vorteile der Überladung von Operatoren, ohne dass magische Operatoren überladen sind - nur Standardtypklassen.Ein weiterer Unterschied besteht darin, dass Sie Methoden verwenden können, die auf einem Typ basieren, auch wenn Sie noch keinen konkreten Wert dieses Typs haben!
Zum Beispiel
read :: Read a => String -> a
. Wenn Sie also genügend andere Typinformationen darüber haben, wie Sie das Ergebnis eines "Lesens" verwenden, können Sie den Compiler herausfinden lassen, welches Wörterbuch für Sie verwendet werden soll.Sie können auch Dinge tun, mit
instance (Read a) => Read [a] where...
denen Sie eine Leseinstanz für definieren können beliebige Liste lesbarer Dinge definieren können. Ich denke nicht, dass das in Java gut möglich ist.Und das alles sind nur Standard-Einzelparameter-Typklassen ohne Tricks. Sobald wir Typklassen mit mehreren Parametern einführen, eröffnet sich eine völlig neue Welt von Möglichkeiten, insbesondere mit funktionalen Abhängigkeiten und Typfamilien, mit denen Sie viel mehr Informationen und Berechnungen in das Typsystem einbetten können.
quelle