Funktionale Programmierung mit Haskell/ Typklassen

Aus Wikibooks

Die Standard-Typklassen[Bearbeiten]

Hier sind alle Typklassen aufgeführt, die auch von Bool verwendet werden. Diese Typklassen werden mit dem Befehl deriving (engl. ableiten) an die data-Anweisung gebunden und geben dem Datentyp bestimmte Eigenschaften mit. Die Typklassen können mit :i genauer untersucht werden.


Typklasse Show[Bearbeiten]

Show ermöglicht es, dass die type constructors wie Zwei und As ausgegeben werden können:

Prelude> data Wert = Zwei|Drei|Vier|Fuenf|Sechs|Siebe|Acht|Neun|Zehn|Bube|Dame|Koenig|As deriving (Show)
Prelude> show As
"As"

Jede Klasse des Moduls Prelude muss vom Typ Show sein, denn Prelude wird vom Interpreter ghci verwendet, und im Interpreter müssen selbstverständlich alle Werte anzeigbar sein.


Typklasse Bounded[Bearbeiten]

Bounded sorgt dafür, dass die Aufzählung einen unteren Wert (hier: Zwei) und einen oberen Wert (hier As) besitzt:

Prelude> data Wert = Zwei|Drei|Vier|Fuenf|Sechs|Siebe|Acht|Neun|Zehn|Bube|Dame|Koenig|As deriving (Show, Bounded)
Prelude> minBound :: Wert
Zwei
Prelude> maxBound :: Wert
As

Diese Ausgabe kann nur funktionieren, wenn die Datentypen auch von Show abgeleitet werden. Sonst haben sie zwar Begrenzungen, aber sie können nicht angezeigt werden. Die Datentypen Bool, Int und Char haben übrigens auch Ober- und Untergrenzen, die auf diese Weise abgefragt werden können:

Prelude> minBound :: Bool 
False
Prelude> maxBound :: Bool 
True
Prelude> minBound :: Int
-2147483648
Prelude> maxBound :: Int
2147483647
Prelude> minBound :: Char
'\NUL'
Prelude> maxBound :: Char
'\1114111'
Prelude>

Für andere Datentypen wie Integer, Float oder Double ist eine obere und untere Begrenzung nicht sinnvoll.


Typklasse Enum[Bearbeiten]

Die Typklasse Enum hilft, den Vorgänger (engl. predecessor) und den Nachfolger (engl. successor) einer Ausprägung anzuzeigen. Das klappt z.B. auch bei Bool und Int:

Prelude> data Wert = Zwei|Drei|Vier|Fuenf|Sechs|Siebe|Acht|Neun|Zehn|Bube|Dame|Koenig|As deriving (Show, Enum)
Prelude> succ Fuenf
Sechs
Prelude> pred As
Koenig
Prelude> [Zwei .. As]       -- Mit Enum kann man auch solche Listen generieren
[Zwei,Drei,Vier,Fuenf,Sechs,Sieben,Acht,Neun,Zehn,Bube,Dame,Koenig,As]
Prelude> succ As            -- Die As hat keinen Nachfolger
*** Exception: succ{Wert}: tried to take `succ' of last tag in enumeration
Prelude> succ False
True
Prelude> pred True
False
Prelude> pred 0
-1
Prelude> succ 0
1
Prelude>

Mit Enum und list comprehension lässt sich auch schon ein Kartensatz generieren:

Prelude> data Wert = Zwei|Drei|Vier|Fuenf|Sechs|Sieben|Acht|Neun|Zehn|Bube|Dame|Koenig|As deriving (Show, Enum)
Prelude> data Farbe = Kreuz|Herz|Pik|Karo deriving (Show, Enum)
Prelude> [(x,y)|x<-[Kreuz .. Karo], y<-[Zwei .. As]]
[(Kreuz,Zwei),(Kreuz,Drei),(Kreuz,Vier),(Kreuz,Fuenf),(Kreuz,Sechs),(Kreuz,Sieben),
(Kreuz,Acht),(Kreuz,Neun),(Kreuz,Zehn),(Kreuz,Bube),(Kreuz,Dame),(Kreuz,Koenig),(Kreuz,As),
(Herz,Zwei),(Herz,Drei),(Herz,Vier),(Herz,Fuenf),(Herz,Sechs),(Herz,Sieben),(Herz,Acht),
(Herz,Neun),(Herz,Zehn),(Herz,Bube),(Herz,Dame),(Herz,Koenig),(Herz,As),(Pik,Zwei),
(Pik,Drei),(Pik,Vier),(Pik,Fuenf),(Pik,Sechs),(Pik,Sieben),(Pik,Acht),(Pik,Neun),(Pik,Zehn),
(Pik,Bube),(Pik,Dame),(Pik,Koenig),(Pik,As),(Karo,Zwei),(Karo,Drei),(Karo,Vier),
(Karo,Fuenf),(Karo,Sechs),(Karo,Sieben),(Karo,Acht),(Karo,Neun),(Karo,Zehn),(Karo,Bube),
(Karo,Dame),(Karo,Koenig),(Karo,As)]
Prelude> length [(x,y)|x<-[Kreuz .. Karo], y<-[Zwei .. As]] -- sind es wirklich 52 Karten?
52
Prelude>
Hinweis: die Zeilenumbrüche in der Ausgabe wurden hier zur besseren Lesbarkeit per Hand einfgefügt.

Typklasse Eq[Bearbeiten]

Mit Eq (engl equation, Ausgleich) können Ausprägungen auf Gleichheit und Ungleichheit verglichen werden:

Prelude> data Wert = Zwei|Drei|Vier|Fuenf|Sechs|Siebe|Acht|Neun|Zehn|Bube|Dame|Koenig|As deriving (Show, Eq)
Prelude> Zwei == Drei
False
Prelude> Zwei /= As                            -- das ist das Ungleichheits-Zeichen in Haskell
True
Prelude> Fuenf >= Zehn

<interactive>:10:7:
    No instance for (Ord Wert) arising from a use of `>='
    Possible fix: add an instance declaration for (Ord Wert)
    In the expression: Fuenf >= Zehn
    In an equation for `it': it = Fuenf >= Zehn

Am letzten Beispiel Fuenf >= Zehn erkennt man, dass für Größer- und Kleiner-Vergleiche eine andere Typklasse zuständig ist.


Typklasse Ord[Bearbeiten]

Ord ist nur zusammen mit Eq arbeitsfähig und hat die Aufgabe, Größer- oder Kleiner-Vergleich zu ermöglichen:

Prelude> data Wert = Zwei|Drei|Vier|Fuenf|Sechs|Siebe|Acht|Neun|Zehn|Bube|Dame|Koenig|As deriving (Show, Eq, Ord)
Prelude> Fuenf >= Zehn
False
Prelude> Fuenf < Zehn
True
Prelude> compare Bube As      -- Vergleich auf Kleiner als
LT
Prelude> compare As Bube      -- Vergleich auf Groesser als
GT
Prelude> compare Neun Neun      -- Vergleich auf Gleichheit
EQ


Typklasse Read[Bearbeiten]

Die Klasse Read ist das Gegenstück von Show und ermöglicht das Einlesen einer Ausprägung. Details hierzu werden im Kapitel I/O-Funktionen erklärt:

Prelude> data Wert = Zwei|Drei|Vier|Fuenf|Sechs|Siebe|Acht|Neun|Zehn|Bube|Dame|Koenig|As deriving (Show, Read)
Prelude> read "Dame" :: Wert
Dame
Prelude>


Hierarchie der Standard Haskell Klassen[Bearbeiten]

Die Haskell Klassen stehen in folgender Hierarchie zueinander:

Überblick über die bisher besprochenen Standard Haskell Klassen

Die Klassen lassen sich im ghci mit :i genauer untersuchen.

Die blauen Typklassen wurden bereits erklärt, aber Haskell kennt auch viele verschiedene Klassen für numerische Werte:

  • Num ist die Oberklasse für jeden beiliebigen Zahlenwert. Jeder Zahlenwert kann angezeigt werden (daher die Ableitung von Show und jeder Zahlenwert kann mit jedem anderen Zahlenwert verglichen werden (Eq).
  • Integral steht für die ganzen Zahlen Integer und Int. Diese Zahlen sind aufzählbar (d.h. man kann den Vorgänger und den Nachfolger bestimmen), daher besitzen sie als Überklasse Enum.