Diese Aussage gilt vielmehr nur für die a, für die es eine elementweise Vergleichsoperation "(<=) :: a -> a -> Bool" gibt. Die Menge aller dieser Typen können wir zu einer Typklasse zusammenfassen. Diese ist bereits vordefiniert und heißt "Ord", und der prinzipielle Typ von msort sieht so aus: "msort :: Ord a => [a] -> [a]". Das lesen wir als "forall a in Ord: msort :: [a] -> [a]". Wo kommt die Klasse eigentlich her? Nun, msort benutzt die Funktion (<=), und die hat den Typ "Ord a => a -> a -> Bool".
class Eq a where (==) :: a -> a -> BoolWir sehen den Klassen-Namen sowie die Klassen-Methode(n).
Wir können einen algebraischen Datentyp (aber kein Typsynonym!) zur Instanz einer Klasse erklären, indem wir die Methode(n) implementieren:
instance Eq Bool where True == True = True False == False = True False == True = False True == False = False
Klassen können eine Hierarchie bilden:
class Eq a => Ord a where (<=) :: a -> a -> BoolBevor man einen Typ t zur Instanz von Ord macht, muß man eine Instance von Eq definieren.
Instanz-Deklarationen können selbst generisch sein, und von Klassen-Constraints abhängen:
instance (Eq a, Eq b) => Eq (a,b) where (c,d) == (e,f) = c == e && d == f instance Eq a => Eq [a] where [] == [] = True (x : xs) == (y : ys) = x == y && xs == ys _ == _ = False
Neben den schon gezeigten Eq und Ord haben wir Enum (aufzählbare Typen), dazu gehören zum Beispiel Char, deswegen geht [ 'a' .. 'z' ], und die Klasse Show. Sie enthält alle Typen, für die es eine sinnvolle druckbare Darstellung gibt. Wir würden so etwas erwarten:
class Show a where show :: a -> StringEs ist aber aus Effizienzgründen anders gelöst. Wenn wir nämlich viele Dinge anzeigen, müssen wir sie mit (++) zusammenkleben, und dabei fleißig Speicherplatz verschwenden, um das erste Argument zu kopieren. Effizienter ist es, nicht den String zu erzeugen, sondern eine Funktion, die den auszugebenden String vor einen anderen String schreibt:
class Show a where shows :: a -> (String -> String)Dann ist show selbst keine Methode, sondern eine Funktion
show :: Show a => a -> String show x = shows x ""Das ist immer noch nicht die ganze Wahrheit, tatsächlich bekommt shows einen zusätzlichen Parameter, die Präzedenz, den man abfragen kann, um zum Beispiel Klammern um Teilausdrücke zu setzen oder wegzulassen. (Ein Beispiel folgt bei der Ausgabe von Bäumen.)
class Show a where showsPrec :: Int -> a -> (String -> String)Der Interpreter Hugs benutzt die Show-Instanzen: wir tippen ja jeweils einen Ausdruck ein, der Interpreter berechnet dessen Wert, wendet auf diesen Wert die show-Funktion an, und druckt den resultierenden String. Deswegen sehen wir manchmal Fehlermeldungen "cannot find show function..."
msort :: Ord a => [a] -> [a]übergeben wir bei jedem Aufruf als neues erstes Argument die zu benutzende Funktion (<=) :: a -> a -> Bool:
msort :: (a -> a -> Bool) -> [a] -> [a]Damit haben wir eine voll polymorphe Funktion vor uns. Genau diese Transformation führt der Haskell-Compiler aus: für jedes Klassen-Constraint im Typ eines Ausdrucks erfindet er ein zusätzliches Argument, auf dem die (Adresse der) Methodentabelle (der Instanz) transportiert wird.
Bemerkung: das bedeutet auch, daß das Klassensystem die Typprüfung nicht wesentlich verändert. Insbesondere bleiben Laufzeitfehler weiterhin ausgeschlossen.