|
Übersicht: Verwandte Themen
Einleitung (Verwandte Themen)
In diesem Abschnitt werden zwei Konzepte vorgestellt, welche
nicht mehr zu den GADTs gehören. Beide Konzepte verfolgen jedoch
eine ähnliche Idee wie die GADTs, nämlich die Erweiterung des
Typsystems. Daher werden diese hier kurz vorgestellt.
Associated Types
Associated Types (etwa gekoppelte Typen), sind Funktionen über
Typen, welche in verschiedenen Instanzen einer Typklasse
verschiedene Werte annehmen können. Da es sich um Funktionen
über Typen handelt, sind Werte hier auch wiederum Typen. Solche
Funktionen haben Ähnlichkeit zu Typkonstruktoren.
Wir betrachtet zunächst die
vordefinierte Num -Typklasse, sowie die Instanz für
den Typ Int :
class Num a where
(+++) :: a -> a -> a
instance Num Int where
(+++) x y = x + y
Wir wählen hier den Operator +++ , um dem
vordefinierten + -Operator nicht in die Quere zu
kommen.
Dieser Operator hat die Einschränkung, dass alle Parametertypen
sowie der Rückgabetyp immer identisch sind. Das bedeutet, dass
beispielsweise keine Int s und Float s
addiert werden können.
Mit Associated Types können wir eine Typklasse definieren,
welche verschiedene Typen addieren kann:
class MyNum a b where
type Sum a b :: *
(+++) :: a -> b -> Sum a b
Diese Typklasse hat zwei Parameter, so dass wir verschiedene
Typen addieren können. Für den Rückgabetyp wurde eine
Funktion Sum definiert. Diese Funktion bekommt die
Typen a und b als Parameter und gibt
einen Typ, hier syntaktisch als * dargestellt,
zurück.
Wir definieren uns nun zunächst eine Instanz
für Int s:
instance MyNum Int Int where
type Sum Int Int = Int
(+++) x y = x + y
Da der Rückgabetyp von einer Addition zweier Int s
wieder eine Int sein soll, gibt
die Sum -Funktion Int zurück.
Der Rückgabetyp hängt von der jeweiligen Instanz ab; Der Typ ist
also an die Instanz gekoppelt.
Die Parameter der Typfunktion liegen bereits durch die
Typparameter fest. An dieser Stelle ist daher etwas
Code-Duplizierung nötig.
Den eigentlichen Nutzen der Typfunktion zeigt das folgende
Beispiel:
instance Integral a => MyNum Float a where
type Sum Float a = Float
(+++) x y = x + intToFloat y
Mit dieser Instanz ist es nun möglich, ein Float
und einen Int (genauer: Integral ) zu
Addieren. Als Rückgabetyp wurde hier Float
gewählt.
Bis jetzt muss allerdings der Float auf der rechten
Seite stehen. Da die Addition kommutativ ist, können wir dies
leicht durch eine zweite Instanz lösen:
instance Integral a => MyNum a Float where
type Sum a Float = Float
(+++) x y = y +++ x
Nun sind auch Ausdrücke wie 42 + 1.5 gültig.
Dependent Types
Eine andere Erweiterung des Typsystems bietet die Sprache
Agda. Hier werden auch viele Probleme gelöst, welche bei den
GADTs entstanden sind.
Unter "Dependent Types" versteht man Typen, die von Werten
abhängen. Der tatsächliche Typ steht also erst zur Laufzeit
fest.
Ein relativ willkürliches Beispiel verdeutlicht das Prinzip:
q : Σ &8469; (λx → if x = 1 then &8469; else Bool)
q ist hier ein Paar. Das erste Element ist einen
natürliche Zahl (&8469;). Der Typ des zweiten Elements wird durch
eine Funktion bestimmt. Diese Funktion gibt &8469;
zurück, falls der Wert des ersten Elements 1
ist. Ansonsten wird Bool zurückgegeben.
q kann nun beispielsweise folgende Werte annehmen:
q = 4, true
q = 1, 42
Mit diesem Konzept sind noch genauere Typüberprüfungen und
Invarianten möglich. Anwendungsfälle sind beispielsweise Binäre
Suchbäume; hier muss der linke Teilbaum stets kleinere Werte als
der rechte Teilbaum enthalten. Auch eine Invariante für
Rot-Schwarz-Bäume lässt sich in Agda formulieren.
Problematisch hierbei ist jedoch die Vermischung von Typen und
Werten. Dadurch dass beliebige Funktionen definierbar sind kann
es zu Endlosschleifen im Typsystem kommen. Das bedeutet, dass
ein korrekt implementierter Compiler bei bestimmten Typen in
eine Endlosschleife gerät. Die Begründung dafür liefert das
Halteproblem: Es ist nicht möglich für alle Programme zu
entscheiden, ob diese bei allen Eingaben anhalten. Dies ist nur
für triviale Programme möglich.
Ein anderes Problem bei Dependent Types ist die
Typgleichheit. Es ist nun viel komplizierter zu testen, ob zwei
Typen gleich sind.
|