Wie wird ein polymorpher Typ in Haskell basierend auf den Ergebnissen der String-Analyse zurückgegeben?

8

TL; DR:
Wie kann ich eine Funktion schreiben, die in ihrem Rückgabetyp polymorph ist? Ich arbeite an einer Übung, bei der es darum geht, eine Funktion zu schreiben, die in der Lage ist, String zu analysieren und je nach Inhalt entweder Vector [Int] , Vector [Char] oder Vector [String] zu generieren.

Längere Version:
Hier sind ein paar Beispiele, wie sich die beabsichtigte Funktion verhalten würde:

  • Die Zeichenfolge "1 2\n3 4" würde ein Vector [Int] generieren, das aus zwei Listen besteht: [1,2] und [3,4] .

  • Die Zeichenkette "'t' 'i' 'c'\n't' 'a' 'c'\n't' 'o' 'e'" würde eine Vector [Char] erzeugen (d. h. bestehend aus den Listen "tic" , "tac" und "toe" ).

  • Die Zeichenfolge "\"hello\" \"world\"\n\"monad\" \"party\"" würde Vector [String] (d. h. ["hello","world"] und ["monad","party"] ) generieren.

Die Fehlerprüfung / Ausnahmebehandlung ist für diese bestimmte Übung kein Problem. In diesem Stadium werden alle Tests rein durchgeführt, d. H. Dies liegt nicht im Bereich von IO monad.

Was ich bisher habe:

Ich habe eine Funktion (und einen neuen Datentyp), die in der Lage ist, einen String zu klassifizieren. Ich habe auch Funktionen (eine für jedes Int , Char und String ), die die Zeichenfolge in den erforderlichen Vektor konvertieren können.

Meine Frage: Wie kann ich diese drei Konvertierungsfunktionen in einer einzigen Funktion kombinieren?

Was ich versucht habe:

  • (Es prüft natürlich nicht, ob ich die drei Conversions stopfe funktioniert in einer einzigen Funktion (d. h. unter Verwendung einer case..of -Struktur Mustervergleich für VectorType der Zeichenfolge.

  • Ich habe versucht, eine Vectorable -Klasse zu erstellen und für jeden Typ eine eigene Instanz zu definieren. Ich erkannte schnell, dass dieser Ansatz nur funktioniert, wenn sich die Argumente der Funktionen nach Typ unterscheiden. In unserem Fall ändert sich der Typ des Arguments nicht (d. H. Es ist immer ein String ).

Mein Code:

Ein paar Kommentare

  • Parsen: Das mySplitter -Objekt und die mySplit -Funktion behandeln das Parsing. Es ist zugegebenermaßen ein grober Parser basierend auf dem Splitter Typ und der split Funktion von Data.List.Split.Internals .

  • Klassifizieren: Die classify -Funktion ist in der Lage, den endgültigen VectorType basierend auf der Zeichenfolge zu bestimmen.

  • Konvertieren: Die Funktionen toVectorNumber , toVectorChar und toVectorString können eine Zeichenfolge in den Typ Vector [Int] , Vector [Char] bzw. Vector [String] konvertieren.

  • Als Nebenbemerkung probiere ich CorePrelude basierend auf einer Empfehlung eines Mentors aus. Deshalb werden Sie sehen, dass ich die verallgemeinerten Versionen der normalen Prelude-Funktionen verwende.

Code:

%Vor%     
iceman 14.11.2014, 23:41
quelle

2 Antworten

16

Sie können nicht.

Kovarianter Polymorphismus wird in Haskell nicht unterstützt und wäre in diesem Fall nicht nützlich.

Das ist im Grunde alles, was es zu beantworten gibt. Nun, warum das so ist.

Es ist nicht gut, einen polymorphen Wert wie OO-Sprachen zu erhalten, weil der einzige Grund, einen Wert zurückzugeben, darin besteht, ihn in anderen Funktionen zu verwenden . Nun, in OO-Sprachen haben Sie keine Funktionen, sondern Methoden, die mit dem Objekt kommen, so dass es ganz einfach ist, "verschiedene Typen zurückzugeben": Jeder wird seine geeigneten Methoden eingebaut haben, und sie können pro Instanz variieren. (Ob das eine gute Idee ist, ist eine andere Frage.)

Aber in Haskell kommen die Funktionen von anderswo. Sie kennen Implementierungsänderungen für eine bestimmte Instanz nicht. Daher können diese Funktionen nur dann sicher definiert werden, wenn jede mögliche Implementierung bekannt ist. Aber wenn Ihr Rückgabetyp wirklich polymorph ist, ist das nicht möglich, weil Polymorphismus ein "offenes" Konzept ist (es erlaubt, dass später neue Implementierungsvarianten hinzugefügt werden).

Stattdessen hat Haskell einen sehr praktischen und völlig sicheren Mechanismus, um eine geschlossene Menge von "Instanzen" zu beschreiben - Sie haben sie bereits selbst benutzt! ADTs.

%Vor%

Das ist der Rückgabetyp, den Sie möchten. Die Funktion wird als solche nicht polymorph sein, sie wird einfach einen vielseitigen Typ zurückgeben.

Wenn Sie darauf bestehen, sollte es polymorph sein

Jetzt ... tatsächlich hat Haskell eine Möglichkeit, mit "polymorphen Rückgaben" umzugehen. Wie in OO, wenn Sie angeben, dass Sie eine Unterklasse einer angegebenen Klasse zurückgeben. Nun, Sie können in Haskell überhaupt keine Klasse zurückgeben, Sie können nur Typen zurückgeben. Aber diese können dazu gebracht werden, "jede Instanz von ..." auszudrücken. Es heißt existenzielle Quantifizierung .

%Vor%

Ich weiß nicht, ob dir das interessant erscheint. Die Wahrheit ist, es ist viel komplizierter und eher schwieriger zu verwenden; Sie können nicht nur irgendeines der möglichen Ergebnisse direkt eingeben, sondern können nur die Elemente mit den Methoden von YourVElemClass verwenden. GADTs können in einigen Anwendungen sehr nützlich sein, aber diese beinhalten normalerweise Klassen mit sehr tiefer mathematischer Motivation. YourVElemClass scheint eine solche Motivation nicht zu haben, so dass Sie mit einer einfachen ADT-Alternative viel besser dran sind als mit einer existenziellen Quantifizierung.

Es gibt eine berühmte Tirade gegen die Existenz von Luke Palmer (beachte er verwendet eine andere Syntax, existenzialspezifisch, was ich für obsolet halte, da GADTs grundsätzlich allgemeiner sind.

    
leftaroundabout 14.11.2014, 23:57
quelle
8

Einfach, verwenden Sie einen Summen-Typ!

%Vor%     
rampion 15.11.2014 00:23
quelle