In einer data
Erklä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. Colour
ist ein Typ und Green
ein Konstruktor, der einen Wert vom Typ enthält Colour
. Ebenso Red
und Blue
sind 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 RGB
ist kein Wert - es ist eine Funktion drei Ints nehmen und Rückkehr Wert! RGB
hat den Typ
RGB :: Int -> Int -> Int -> Colour
RGB
ist 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 RGB
wir einen Farbwert , wenn wir auf drei Werte anwenden !
Prelude> RGB 12 92 27
#0c5c1b
Wir haben einen Wert vom Typ Colour
konstruiert, 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 String
s 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 Leaf
und Branch
), die Werte des SBTree
Typs 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 String
s auf irgendeine Weise speichert .
Wir sehen auch, dass beide Datenkonstruktoren ein String
Argument annehmen - dies ist der String, den sie im Baum speichern werden.
Aber! Was wäre, wenn wir auch speichern könnten, müssten Bool
wir einen neuen Binärbaum erstellen. Es könnte ungefähr so aussehen:
data BBTree = Leaf Bool
| Branch Bool BBTree BBTree
Typkonstruktoren
Beide SBTree
und BBTree
sind 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 BTree
ist 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 Bool
beispielsweise als Argument an übergeben BTree
, wird der Typ zurückgegeben BTree Bool
, bei dem es sich um einen Binärbaum handelt, in dem Bool
s gespeichert ist . Ersetzen Sie jedes Vorkommen der Typvariablen a
durch den Typ Bool
, und Sie können selbst sehen, wie es wahr ist.
Wenn Sie möchten, können Sie BTree
als Funktion mit der Art anzeigen
BTree :: * -> *
Arten sind etwas wie Typen - das *
zeigt einen konkreten Typ an, also sagen wir, es BTree
handelt 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 a
Typ berücksichtigen . Seine Definition ist
data Maybe a = Nothing
| Just a
Hier Maybe
ist ein Typkonstruktor, der einen konkreten Typ zurückgibt. Just
ist ein Datenkonstruktor, der einen Wert zurückgibt. Nothing
ist 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, Just
nimmt einen Wert vom Typ an a
und gibt einen Wert vom Typ zurück Maybe a
. Wenn wir uns die Art von ansehen Maybe
, sehen wir das
Maybe :: * -> *
Mit anderen Worten, Maybe
nimmt 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 Maybe
s erstellen - wenn Sie versuchen, auszuführen
[] :: [Maybe]
Sie erhalten eine Fehlermeldung. Sie können jedoch eine Liste von Maybe Int
oder erstellen Maybe a
. Das liegt daran, dass Maybe
es sich um eine Typkonstruktorfunktion handelt, eine Liste jedoch Werte eines konkreten Typs enthalten muss. Maybe Int
und Maybe a
sind konkrete Typen (oder, wenn Sie möchten, Aufrufe, Typkonstruktorfunktionen einzugeben, die konkrete Typen zurückgeben.)
Car
es sich sowohl um einen Typkonstruktor (auf der linken Seite=
) als auch um einen Datenkonstruktor (auf der rechten Seite) handelt. Im ersten BeispielCar
akzeptiert der Typkonstruktor keine Argumente, im zweiten Beispiel drei. In beiden Beispielen verwendet derCar
Datenkonstruktor drei Argumente (die Typen dieser Argumente sind jedoch in einem Fall festgelegt und in dem anderen parametrisiert).