In einer dataErklärung, ein Typkonstruktor ist das , was auf der linken Seite des Gleichheitszeichens. Die Datenkonstruktoren sind die Dinge auf der rechten Seite des Gleichheitszeichens. Sie verwenden Typkonstruktoren, bei denen ein Typ erwartet wird, und Sie verwenden Datenkonstruktoren, bei denen ein Wert erwartet wird.
Datenkonstruktoren
Zur Vereinfachung können wir mit einem Beispiel eines Typs beginnen, der eine Farbe darstellt.
data Colour = Red | Green | Blue
Hier haben wir drei Datenkonstruktoren. Colourist ein Typ und Greenein Konstruktor, der einen Wert vom Typ enthält Colour. Ebenso Redund Bluesind beide Konstruktoren, die Werte vom Typ konstruieren Colour. Wir könnten uns vorstellen, es aufzupeppen!
data Colour = RGB Int Int Int
Wir haben nach wie vor nur die Art Colour, aber RGBist kein Wert - es ist eine Funktion drei Ints nehmen und Rückkehr Wert! RGBhat den Typ
RGB :: Int -> Int -> Int -> Colour
RGBist ein Datenkonstruktor, bei dem es sich um eine Funktion handelt, die einige Werte als Argumente verwendet und diese dann verwendet, um einen neuen Wert zu erstellen . Wenn Sie objektorientiert programmiert haben, sollten Sie dies erkennen. In OOP nehmen Konstruktoren auch einige Werte als Argumente und geben einen neuen Wert zurück!
In diesem Fall erhalten RGBwir einen Farbwert , wenn wir auf drei Werte anwenden !
Prelude> RGB 12 92 27
#0c5c1b
Wir haben einen Wert vom Typ Colourkonstruiert, indem wir den Datenkonstruktor angewendet haben. Ein Datenkonstruktor enthält entweder einen Wert wie eine Variable oder verwendet andere Werte als Argument und erstellt einen neuen Wert . Wenn Sie zuvor programmiert haben, sollte Ihnen dieses Konzept nicht sehr fremd sein.
Pause
Wenn Sie einen Binärbaum zum Speichern von Strings erstellen möchten , können Sie sich vorstellen, so etwas zu tun
data SBTree = Leaf String
| Branch String SBTree SBTree
Was wir hier sehen, ist ein Typ SBTree, der zwei Datenkonstruktoren enthält. Mit anderen Worten, es gibt zwei Funktionen (nämlich Leafund Branch), die Werte des SBTreeTyps konstruieren . Wenn Sie nicht mit der Funktionsweise von Binärbäumen vertraut sind, bleiben Sie einfach dran. Sie müssen eigentlich nicht wissen, wie Binärbäume funktionieren, nur dass dieser Strings auf irgendeine Weise speichert .
Wir sehen auch, dass beide Datenkonstruktoren ein StringArgument annehmen - dies ist der String, den sie im Baum speichern werden.
Aber! Was wäre, wenn wir auch speichern könnten, müssten Boolwir einen neuen Binärbaum erstellen. Es könnte ungefähr so aussehen:
data BBTree = Leaf Bool
| Branch Bool BBTree BBTree
Typkonstruktoren
Beide SBTreeund BBTreesind Typkonstruktoren. Aber es gibt ein eklatantes Problem. Sehen Sie, wie ähnlich sie sind? Das ist ein Zeichen dafür, dass Sie wirklich irgendwo einen Parameter wollen.
Also können wir das tun:
data BTree a = Leaf a
| Branch a (BTree a) (BTree a)
Jetzt führen wir eine Typvariable a als Parameter in den Typkonstruktor ein. In dieser Erklärung BTreeist eine Funktion geworden. Es nimmt einen Typ als Argument und gibt einen neuen Typ zurück .
Hier ist es wichtig , den Unterschied zwischen einem betrachten konkreter (Beispiele umfassen Int, [Char]und Maybe Bool) , die ein Typ ist, der auf einen Wert in Ihrem Programm zugeordnet werden kann, und eine Typkonstruktor Funktion , die Sie brauchen eine Art zu ernähren zu können sein einem Wert zugeordnet. Ein Wert kann niemals vom Typ "Liste" sein, da es sich um eine "Liste von etwas " handeln muss. Im gleichen Sinne kann ein Wert niemals vom Typ "Binärbaum" sein, da es sich um einen "Binärbaum handeln muss, der etwas speichert ".
Wenn wir Boolbeispielsweise als Argument an übergeben BTree, wird der Typ zurückgegeben BTree Bool, bei dem es sich um einen Binärbaum handelt, in dem Bools gespeichert ist . Ersetzen Sie jedes Vorkommen der Typvariablen adurch den Typ Bool, und Sie können selbst sehen, wie es wahr ist.
Wenn Sie möchten, können Sie BTreeals Funktion mit der Art anzeigen
BTree :: * -> *
Arten sind etwas wie Typen - das *zeigt einen konkreten Typ an, also sagen wir, es BTreehandelt sich von einem konkreten Typ zu einem konkreten Typ.
Einpacken
Treten Sie einen Moment zurück und beachten Sie die Ähnlichkeiten.
Ein Datenkonstruktor ist eine "Funktion", die 0 oder mehr Werte annimmt und Ihnen einen neuen Wert zurückgibt.
Ein Typkonstruktor ist eine "Funktion", die 0 oder mehr Typen akzeptiert und Ihnen einen neuen Typ zurückgibt.
Datenkonstruktoren mit Parametern sind cool, wenn wir geringfügige Abweichungen in unseren Werten wünschen. Wir setzen diese Abweichungen in den Parametern ein und lassen den Typ, der den Wert erstellt, entscheiden, welche Argumente sie eingeben. Im gleichen Sinne sind Typkonstruktoren mit Parametern cool wenn wir leichte Abweichungen in unseren Typen wollen! Wir setzen diese Variationen als Parameter und lassen den Typ, der den Typ erstellt, entscheiden, welche Argumente er eingeben wird.
Eine Fallstudie
Als Heimstrecke hier können wir den Maybe aTyp berücksichtigen . Seine Definition ist
data Maybe a = Nothing
| Just a
Hier Maybeist ein Typkonstruktor, der einen konkreten Typ zurückgibt. Justist ein Datenkonstruktor, der einen Wert zurückgibt. Nothingist ein Datenkonstruktor, der einen Wert enthält. Wenn wir uns die Art von ansehen Just, sehen wir das
Just :: a -> Maybe a
Mit anderen Worten, Justnimmt einen Wert vom Typ an aund gibt einen Wert vom Typ zurück Maybe a. Wenn wir uns die Art von ansehen Maybe, sehen wir das
Maybe :: * -> *
Mit anderen Worten, Maybenimmt einen konkreten Typ und gibt einen konkreten Typ zurück.
Noch einmal! Der Unterschied zwischen einer konkreten Typ- und einer Typkonstruktorfunktion. Sie können keine Liste von Maybes erstellen - wenn Sie versuchen, auszuführen
[] :: [Maybe]
Sie erhalten eine Fehlermeldung. Sie können jedoch eine Liste von Maybe Intoder erstellen Maybe a. Das liegt daran, dass Maybees sich um eine Typkonstruktorfunktion handelt, eine Liste jedoch Werte eines konkreten Typs enthalten muss. Maybe Intund Maybe asind konkrete Typen (oder, wenn Sie möchten, Aufrufe, Typkonstruktorfunktionen einzugeben, die konkrete Typen zurückgeben.)
Cares sich sowohl um einen Typkonstruktor (auf der linken Seite=) als auch um einen Datenkonstruktor (auf der rechten Seite) handelt. Im ersten BeispielCarakzeptiert der Typkonstruktor keine Argumente, im zweiten Beispiel drei. In beiden Beispielen verwendet derCarDatenkonstruktor drei Argumente (die Typen dieser Argumente sind jedoch in einem Fall festgelegt und in dem anderen parametrisiert).