data Typname (Parameter)* = (Konstruktor (Komponente)*)*
data Shape = Rectangle Side Side | Ellipse Radius Radius | Polygon [ Vertex ]Hier ist Shape der neu definierte algebraische Datentyp, er hat keine Parameter, Rectangle, Ellipse und Polygon sind die Konstruktoren, die ersten beiden sind zweistellig, der letzte ist einstellig.
Die Konstruktoren sind Funktionen, haben also Typen, Beispiel:
Polygon :: [ Vertex ] -> ShapeDie Konstruktoren verwenden wir beim Mustersuchen (SOE, Seite 25 f)
area :: Shape -> Float area (Rectangle x y) = x * y area (Ellipse a b) = pi * a * b area (Polygon ps) = ...In einem Sichtbarkeitsbereich (Modul) darf es jeden Konstruktor nur einmal geben. Das folgende geht also nicht:
data Shape = ... (wie oben) data Round = Circle Radius | Ellipse Radius RadiusDann wüßte man nämlich nicht, welchen Typ der Ausdruck Ellipse 1.0 2.0 haben soll (Shape oder Round). Man könnte das durch Nachschlagen im Kontext herausbekommen, falls der Programmierer eine umgebende Typdeklaration geschrieben hat. Das muß er aber in Haskell (leider) nicht, deswegen geht das nicht.
Zwei wichtige Typen aus der Prelude sind
data Maybe a = Nothing | Just a data Either a b = Left a | Right b
type Typname (Parameter)* = Typ-AusdruckSie haben die gleichen Vor- und Nachteile wie Macros in C: bei sorgsamer Benutzung erhöhen sie die Übersichtlichkeit, aber der Compiler kann das nicht vom Programmierer erzwingen. Beispiel (SOE Seite 24)
type Radius = Float type Side = FloatDie Aussage Rectangle :: Side -> Side -> Shape sieht schön aus, aber Rectangle :: Float -> Radius -> Shape wird vom Compiler genauso akzeptiert.
Mit Typ-Synonymen können wir also Abstraktionen nicht wirklich durchsetzen. Das richtige Mittel dazu sind Algebraischen Datentypen, deren Konstruktoren nicht exportiert werden (siehe später mehr über Module).
data Typname (Parameter)* = ( Konstruktor { name :: Type, ... } )*Das erklärt wie oben eine Menge von Konstruktoren mit entsprechenden Typen und Stelligkeiten und zusätzlich eine Menge von Zugriffsfunktionen.
Beispiel (kompletter Quelltext)
type Winkel = Rational type Winkels = [ Winkel ] data Ecke = Ecke { sichtbar :: Winkel , alle :: Winkels } data Streckenzug = Streckenzug [ Ecke ]Ausdrücke vom Typ Ecke bauen wir so (Beispiel: reguläres Polygon?)
e :: Ecke e = Ecke { sichtbar = 1 % 2, alle = [ 1 % 2 ] }und wir können benannte Muster benutzen:
länge (Ecke { alle = ws }) = length wsDie Namen "sichtbar", "alle" sind auch Zugriffsfunktionen:
sichtbar :: Ecke -> Winkel alle :: Ecke -> WinkelsWir können (leider?) die Konstruktoren mit benannten Komponenten auch in der unbenannten Version benutzen.