Type Classes

Type Classes sind eine Art Kontrakt/Interface (s. Java), die definierte Funktionen einfordern. Funktionen sind in der Type Class über Signaturen definiert, eine Default-Implementierung ist jedoch möglich.

class  Eq a where
  (==), (/=) :: a -> a -> Bool

  -- Minimal complete definition:
  --      (==) or (/=)
  x /= y     =  not (x == y)
  x == y     =  not (x /= y)

Type Classes können auch über mehrere Typ-Parameter verfügen, sie heißen dann Multitype Classes

class Container contType elemType where
  insert :: contType -> elemType -> contType

Type Classes werden von Typen instanziert, die Funktionalität der Class implementieren und zur Verfügung stellen wollen.

instance Eq NieGleichType where
  a == b = False

Type Classes erlauben durch die Implementierung je Typ eine Ad Hoc Polymorphie, die sich darin ausdrückt, dass je Typ verschiedene Implementierungen "ad hoc" benutzt werden. Ad Hoc Polymorphie wird auch Overloading genannt.

eqInt :: Int -> Int -> Bool
eqInt a b = a == b -- Impl von Eq Int

eqDbl :: Double -> Double -> Bool
eqDbl a b = a == b -- Impl von Eq Double

Typen können über Constraints eingeschränkt werden. Ein Constraint fordert von einem Typ die Implementierung einer Type Class ein

class (Num a, Eq a) => NumerischeUndVergleichbareTypenAddiererClass a
  addiereWennUngleich :: a -> a -> a
 
instance NumerischeUndVergleichbareTypenAddiererClass Int
  addiereWennUngleich a b
    | a == b    = 0 -- Ad Hoc Polymorphie: Impl von == der instance der Type Class Eq wird benutzt
    | otherwise = a + b

Ihr Browser kann leider keine Haskell-Quelltexte anzeigen

(download Haskell Source)

Functional Dependencies

Functional Dependencies können die Typen einer Multitype Class beschränken. Sie drücken aus, dass ein Typ b eindeutig durch einen anderen Typ a determiniert wird. Durch diese Ausage besteht zwischen den Typen a und b keine Relation, sondern eine funktionale Abhängigkeit (Funktion: Jeden Wert der Defmenge auf genau ein Element der Zielmenge abbilden).

class Container contType elemType | contType -> elemType where
	insert :: contType -> elemType -> contType

Effekt #1 einer Fun Dep: Der Compiler kann den (funktional determinierten) Type über Type Inference überhaupt erst ableiten. Ohne eine Fun Dep bestände eine Mehrdeutigkeit, wogegen mit einer Fun Dep eine eindeutige Abhängigkeit existiert.

Effekt #2 einer Fun Dep: Die möglichen Type Class Implementierung per instance-Keyword werden eingeschränkt. Folgendes ist aufgrund der Fun Dep nicht möglich (wäre sonst eine Relation!):

instance Container [Int] Int where -- OK
  insert cont elem = elem : cont

instance Container [Int] Char where -- [Int] bildet jetzt auf Int (obere instance) und Char ab. Wäre also eine Relation!
  insert cont elem = ord elem : cont

Ihr Browser kann leider keine Haskell-Quelltexte anzeigen

(download Haskell Source)

Type Families

Type Families sind eine Menge von Typen, die durch einen Typ Constructor repräsentiert werden. Sie stellen Funktionen auf Ebene der Typen darf, im Gegensatz zu "gewöhnlichen" Funktionen auf Ebene der Werte. Typ Funktionen werden zur Compilezeit ausgewertet. Type Families werden auch Indexed Type Families genannt, wobei die Indices die Typ Parameter eines Typ Constructors darstellen. Aufgrund dieser indizierten Typen liefert der Typ Constructor zur Compilezeit unterschiedliche Typen zurück.

type family Elem c -- Type Constructor Elem, der aufgrund des Type Index c einen Typ zurückliefert

Type Families haben wie Funktionen einen Typ, der über Kinds * ausgedrückt wird.

type family Elem c :: *       -- äquivalent zu obiger Definition
type family Elem' c :: * -> * -- Type Constructor, der einen Typ Constructor zurückliefert

Type Families drücken wie Functional Dependencies eine Funktionale Abhängigkeit aus. Hier jedoch nicht deklarativ, sondern in Form von (Typ) Funktionen.

Eine Untermenge der Type Families stellen die Associated Type dar. Der einzige Unterschied zu den Type Families ist, dass sie nicht wie diese auf oberste Ebene definiert werden, sondern immer an eine Type Class gebunden sind; deshalb auch "assoziert".

class Container contType where
  type Elem contType :: *
  insert :: contType -> Elem contType -> contType

Es gibt einen impliziten Unterschied zwischen Type Families auf Typen (type) und Algebraischen Datentypen (data): Aus data Deklarationen kann der Compiler eine Injektivität schließen.

class Foo a where
  type Bar a :: *  -- Bar a ~ Bar b => a ~ b (der Schluss auf 'a ist typ-äquivalent zu b' ist möglich)

class Foo' a where
  data Bar' a :: * -- Bar' a ~ Bar' b =/=> a ~ b (der Schluss auf 'a ist typ-äquivalent zu b' ist nicht möglich!)

Ihr Browser kann leider keine Haskell-Quelltexte anzeigen Ihr Browser kann leider keine Haskell-Quelltexte anzeigen Ihr Browser kann leider keine Haskell-Quelltexte anzeigen

(download Haskell Source)

Type Computations

Die Berechnung der Typen zur Compilezeit ist Turing Complete, so dass prinzipiell jeder Algorithmus über Typ-Berechnung definiert werden kann. (s. Brainfuck-Interpreter: [1], [2])

Ihr Browser kann leider keine Haskell-Quelltexte anzeigen

(download Haskell Source)