2016-04-28 11 views
15

Laut Harper (https://existentialtype.wordpress.com/2011/04/16/modules-matter-most/) scheint es, dass Typklassen nicht das gleiche Maß an Abstraktion bieten, das Module bieten, und es fällt mir schwer, genau herauszufinden, warum. Und es gibt keine Beispiele in diesem Link, so dass es schwer für mich ist, die wichtigsten Unterschiede zu sehen. Es gibt auch andere Papiere, wie man zwischen Modulen und Typklassen übersetzt (http://www.cse.unsw.edu.au/~chak/papers/modules-classes.pdf), aber das hat nicht wirklich etwas mit der Implementierung in der Perspektive des Programmierers zu tun (es sagt nur, dass es nichts gibt, was man tun kann, dass andere können nicht emulieren).(ML) Module vs (Haskell) Typ Klassen

Insbesondere im first link:

Die erste ist, dass sie darauf bestehen, dass ein Typ eine Typklasse in genau einer Art und Weise umsetzen können. Zum Beispiel können nach der Philosophie der Typklassen die ganzen Zahlen genau auf eine Weise geordnet werden (die übliche Ordnung), aber offensichtlich gibt es viele Ordnungen (zum Beispiel durch Teilbarkeit) von Interesse. Die zweite besteht darin, dass sie zwei getrennte Probleme durcheinander bringen: Festlegen, wie ein Typ eine Typklasse implementiert und angeben, wann eine solche Spezifikation während der Typinferenz verwendet werden soll.

Ich verstehe auch nicht. Ein Typ kann eine Typklasse auf mehr als 1 Weise in ML implementieren? Wie würden Sie die Ganzzahlen durch Teilbarkeit geordnet haben, ohne einen neuen Typ zu erstellen? In Haskell müssten Sie etwas wie Daten verwenden und haben die instance Ord, um eine alternative Bestellung anzubieten.

Und die zweite, sind nicht die beiden in Haskell eindeutig?

blah :: BlahType b => ... 

wo BlahType die Klasse während der Typinferenz und NICHT der implementierenden Klasse verwendet wird, ist: Die Angabe von „wenn eine solche Angabe bei der Typinferenz verwendet werden sollte,“ kann durch so etwas getan werden. Dagegen wird "wie ein Typ eine Typklasse implementiert" unter Verwendung von instance ausgeführt.

Kann jemand erklären, was der Link wirklich versucht zu sagen? Ich verstehe einfach nicht, warum Module weniger restriktiv sind als Typenklassen.

Antwort

11

Um zu verstehen, was der Artikel sagt, nehmen Sie sich einen Moment Zeit, um die Monoid Typklasse in Haskell zu betrachten. Ein Monoid ist ein beliebiger Typ, T, der eine Funktion mappend :: T -> T -> T und ein Identitätselement empty :: T aufweist, für das Folgendes gilt.

a `mappend` (b `mappend` c) == (a `mappend` b) `mappend` c 
a `mappend` mempty == mempty `mappend` a == a 

Es gibt viele Haskell-Typen, die dieser Definition entsprechen. Ein Beispiel, das sofort in den Sinn kommt, sind die ganzen Zahlen, für die wir folgendes definieren können.

instance Monoid Integer where 
    mappend = (+) 
    mempty = 0 

Sie können bestätigen, dass alle Anforderungen erfüllt sind.

In der Tat, die Bedingungen gelten für alle Zahlen über Addition, so dass wir auch Folgendes definieren können.

instance Num a => Monoid a where 
    mappend = (+) 
    mempty = 0 

So jetzt, in GHCi können wir Folgendes tun.

> mappend 3 5 
8 
> mempty 
0 

Besonders aufmerksame Leser (oder solche mit einem Hintergrund in mathemetics) werden wahrscheinlich schon bemerkt, dass wir auch eine Monoid Instanz für Zahlen über Multiplikation definieren können.

instance Num a => Monoid a where 
    mappend = (*) 
    mempty = 1 

a * (b * c) == (a * b) * c 
a * 1 == 1 * a == a 

Aber jetzt findet der Compiler ein Problem. Welche Definition von mappend soll es für Zahlen verwenden? Ist mappend 3 5 gleich 8 oder 15? Es gibt keine Möglichkeit zu entscheiden. Aus diesem Grund lässt Haskell mehrere Instanzen einer einzelnen Typklasse nicht zu. Das Problem besteht jedoch immer noch. Welche Monoid Instanz von Num sollen wir verwenden? Beide sind vollkommen gültig und für bestimmte Umstände sinnvoll. Die Lösung ist, keines von beiden zu verwenden. Wenn Sie in Hackage Monoid suchen, werden Sie sehen, dass es keine Monoid Instanz von Num oder Integer, Int, Float oder Double für diese Angelegenheit gibt. Stattdessen gibt es Monoid Instanzen von Sum und Product. Sum und Product sind wie folgt definiert.

newtype Sum a = Sum { getSum :: a } 
newtype Product a = Product { getProduct :: a } 

instance Num a => Monoid (Sum a) where 
    mappend (Sum a) (Sum b) = Sum $ a + b 
    mempty = Sum 0 

instance Num a => Monoid (Product a) where 
    mappend (Product a) (Product b) = Product $ a * b 
    mempty = Product 1 

Nun, wenn Sie eine Zahl als Monoid verwenden möchten, müssen Sie es wickeln entweder in einem Sum oder Product Typ. Welcher Typ Sie verwenden, bestimmt, welche Instanz Monoid verwendet wird. Dies ist die Essenz dessen, was der Artikel beschreiben wollte. In das Haskell-Typenklassensystem ist kein System integriert, so dass Sie zwischen mehreren Intakten wählen können. Stattdessen musst du durch Reifen springen und sie in Skeletttypen auspacken. Nun, ob Sie dies als ein Problem betrachten oder nicht, ist ein großer Teil dessen, was bestimmt, ob Sie Haskell oder ML bevorzugen.

ML löst das, indem mehrere "Instanzen" derselben Klasse und desselben Typs in verschiedenen Modulen definiert werden können. Welches Modul Sie importieren, bestimmt dann, welche "Instanz" Sie verwenden. (Streng genommen hat ML keine Klassen und Instanzen, aber es hat Signaturen und Strukturen, die fast gleich funktionieren können. Für einen tieferen Vergleich lesen Sie this paper).

+1

Wie löst ML diese Mehrdeutigkeit? Ja, in Haskell musst du es in einen anderen Typ einpacken. Aber wie geht ML damit um? "Ein Typ kann eine Typklasse in mehr als 1 Weise in ML implementieren? Wie würden Sie die Ganzzahlen durch Teilbarkeit sortiert haben, ohne einen neuen Typ zu erstellen?" wird dadurch nicht wirklich beantwortet. –

+1

@RahulManne überhaupt kein ML-Programmierer *, aber ich glaube, es ermöglicht Ihnen im Wesentlichen "Instanzen" zu nennen und gezielt auszuwählen, welche in den Geltungsbereich zu bringen (durch den Einsatz von erstklassigen Modulen für diesen Zweck anstelle von Klassen). – Ben

+0

@Ben, wenn das der Fall ist, frage ich mich, ist es möglich, eine ähnliche Sache zu tun, wo Sie die Instanz in einer anderen Datei definieren, und das Importieren dieser Datei wird nur diese Instanz verwenden? Ich werde es ausprobieren. –