Zum Inhalt springen
L

Wikipedia · einfach zusammengefasst · Stand

Typklasse (Informatik)

Typklassen sind ein Konstrukt der funktionalen Programmierung zur Implementierung von Ad-hoc-Polymorphie. Typklassen wurden für die Sprache Haskell …

Inhalt5 Abschnitte
  1. 1. Grundidee und Bedeutung
  2. 2. Typklassen und Instanzen
  3. 3. Beispiel mit Bool und allgemeinen Funktionen
  4. 4. Typische Anwendungen
  5. 5. Umsetzung durch zusätzliche Daten und Parameter

Grundidee und Bedeutung

Typklassen sind ein Konstrukt der funktionalen Programmierung zur Umsetzung von Ad-hoc-Polymorphie: Eine Funktion kann für verschiedene Typen jeweils passend implementiert und verwendet werden. Typklassen wurden für Haskell entwickelt. Sie ähneln grundsätzlich Schnittstellen, sind aber keine Klassen im Sinne der objektorientierten Programmierung.

Ursprünglich dienten Typklassen dazu, mathematische Operatoren systematisch zu überladen. Da sich das Konzept als vielseitig erwies, wurde es bald auch für andere Zwecke wie Monaden eingesetzt. Heute gehören Typklassen zu den wichtigsten Werkzeugen Haskells. Sie werden besonders zur Definition gemeinsamer Schnittstellen und zur Verallgemeinerung von Bibliotheken verwendet.

Typklassen und Instanzen

Eine Typklasse legt Funktionen fest, die für jede ihrer Instanzen aufgerufen werden können. Ein Typ wird zur Instanz einer Typklasse, indem die verlangten Funktionen für diesen Typ definiert werden.

Ein grundlegendes Beispiel ist die Typklasse Eq für Gleichheit. Sie besitzt einen Typparameter a und definiert zwei Operatoren:

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

Beide Operatoren nehmen zwei Werte desselben Typs a entgegen und liefern einen booleschen Wert (Bool). (==) prüft auf Gleichheit, (/=) auf Ungleichheit. Operatoren sind in Haskell gewöhnliche Funktionen, werden jedoch in Infixnotation zwischen ihren Argumenten geschrieben.

Eine Typklasse kann Standardimplementierungen bereitstellen. Bei Eq lassen sich die beiden Prüfungen durch gegenseitige Negation definieren:

a == b = not (a /= b) a /= b = not (a == b)

Deshalb muss eine Instanz nur einen der beiden Operatoren implementieren. Eine eigene Implementierung beider Funktionen kann dennoch sinnvoll sein, wenn sie für einen bestimmten Typ effizienter ist.

Beispiel mit Bool und allgemeinen Funktionen

Für den Typ Bool kann eine Eq-Instanz die Gleichheitsprüfung so definieren:

instance Eq Bool where True == True = True False == False = True _ == _ = False

Dabei steht _ für einen nicht weiter benötigten Wert. Die Instanz implementiert nur (==). Die Prüfung (/=) ergibt sich automatisch aus der Standardimplementierung als Negation der Gleichheit.

Eine Funktion muss den konkreten Datentyp ihrer Argumente nicht kennen, um eine Typklassenfunktion anzuwenden. Es reicht, dass für den Typ eine passende Instanz existiert. Ein Typklassenkontext in der Typsignatur drückt diese Voraussetzung aus.

Die Funktion nub entfernt beispielsweise Duplikate aus einer Liste:

nub :: Eq a => [a] -> [a] nub [] = [] nub (x:xs) = x : nub (filter (/= x) xs)

Der Kontext Eq a bedeutet, dass die Listenelemente vom Typ a eine Instanz von Eq besitzen müssen. Dadurch kann nub den Operator (/=) verwenden, ohne den konkreten Elementtyp zu kennen.

Typische Anwendungen

Haskell verwendet Typklassen für zahlreiche allgemeine Schnittstellen:

  • Show definiert die Funktion show, mit der ein Wert als String dargestellt werden kann. Sie ähnelt der Methode toString() in Java.
  • Read definiert die Funktion read, mit der Werte geparst und in einen Datentyp verpackt werden können.
  • Monad stellt eine typübergreifende Syntax für Monaden bereit.

Umsetzung durch zusätzliche Daten und Parameter

Es gibt mehrere Möglichkeiten, Typklassen zu implementieren. Der ursprüngliche und in den meisten Implementierungen – einschließlich des Glasgow Haskell Compiler – verwendete Ansatz ersetzt jede Typklasse durch einen Datentyp. Dessen Felder enthalten die Methoden der Typklasse.

Für Eq kann man sich die Übersetzung so vorstellen:

data Eq a = Eq (a -> a -> Bool) (a -> a -> Bool)

Ein Wert dieses Datentyps enthält also die beiden Funktionen für Gleichheit und Ungleichheit. Für jede Instanz wird ein entsprechender Wert erzeugt. Eine Repräsentation der Bool-Instanz kann etwa eqBool für die Gleichheit enthalten und die Ungleichheit durch \a b -> not (eqBool a b) bilden. Für den Einheitstyp () kann Gleichheit immer True und Ungleichheit immer False liefern.

Benötigt eine Funktion den Kontext Eq a, wird dieser bei der Übersetzung zu einem zusätzlichen Parameter vom Typ Eq a. Aus diesem Parameter entnimmt die Funktion die erforderlichen Methoden. Aus

nub :: Eq a => [a] -> [a]

wird dem Prinzip nach:

nub :: Eq a -> [a] -> [a]

Benötigt eine von nub aufgerufene Funktion denselben Kontext, wird der zusätzliche Wert an sie weitergereicht. Das Typsystem garantiert, dass die darin gespeicherten Funktionen die richtigen Typen besitzen. Für den endgültigen Code ist daher keine Erweiterung des bestehenden Typsystems erforderlich.

Weiterlesen