Typ-Polymorphismus Universität Bielefeld AG Praktische Informatik November 12, 2014
Das Haskell Typ-System Wir beginnen mit einer Wiederholung des Bekannten: In allen Programmiersprachen sind Typ-Konzepte ein prägendes Element. Das Haskell Typ-System hat zwei grundlegende Konzepte: Parametrischer Typ-Polymorphismus Typ-Klassen Andere Konzepte: untypisierte Sprachen monomorphe Typen Overloading Objektklassen und Vererbung
Strong typing Unter strong typing (strenge Typisierung) in einer Programmiersprache versteht man: Alle Typen werden durch den Compiler überprüft Zur Laufzeit der übersetzten Programme können keine Typfehler auftreten Typ-Sicherheit ist erreichbar ohne Effizienzverlust zur Laufzeit
Praktische Bedeutung des Typ-Systems Fehler im Programmentwurf kann man grob unterteilen: Denkfehler : Der Algorithmus löst das vorgegebene Problem nicht Codierfehler : Die algorithmische Idee ist richtig, aber ihre Formulierung im Programm ist fehlerhaft Fehler der zweiten Art äußern sich häufig als Typfehler. Erkennt sie der Compiler, erspart dies viel Mühe beim Testen.
Praktische Bedeutung des Typ-Systems Fehler im Programmentwurf kann man grob unterteilen: Denkfehler : Der Algorithmus löst das vorgegebene Problem nicht Codierfehler : Die algorithmische Idee ist richtig, aber ihre Formulierung im Programm ist fehlerhaft Fehler der zweiten Art äußern sich häufig als Typfehler. Erkennt sie der Compiler, erspart dies viel Mühe beim Testen. Der Programmier-Lehrling schlägt sich mit den Typ-Fehlern herum, der Könner genießt still.
Warum Typ-Polymorphismus? Ziel: Typsicherheit + Wiederverwendbarkeit von Code 1 > length [] = 0 2 > length ( x: xs) = 1 + length xs Was ist der Typ? length kann Listen jeden Typs verarbeiten, weil die Länge der Liste vom Element-Typ unabhängig ist.
Warum Typ-Polymorphismus? Ziel: Typsicherheit + Wiederverwendbarkeit von Code 1 > length [] = 0 2 > length ( x: xs) = 1 + length xs Was ist der Typ? length kann Listen jeden Typs verarbeiten, weil die Länge der Liste vom Element-Typ unabhängig ist. 1 length :: String - > Int 2 length :: [ Int ] -> Int 3 length :: [ Bool ] -> Int 4 length :: [ String ] -> Int 5 length :: [[ String ]] -> Int -- und so weiter...? Es soll nur EINE Definition und EINEN allgemeinen Typ von length geben: length::[a] -> Int.
Monomorphe Typen Monomorpher Typ: vollständig festgelegt, z.b. bei Konstanten 1 True, Note ce (1/2), " abraham ", Just " Justine " 2 Bool, Musik, [ Char ] Maybe String
Monomorphe Typen Monomorpher Typ: vollständig festgelegt, z.b. bei Konstanten 1 True, Note ce (1/2), " abraham ", Just " Justine " 2 Bool, Musik, [ Char ] Maybe String Monomorpher Typ: vollständig festgelegt, z.b. durch Deklaration 1 x:: Integer, y:: Bool, 42:: Integer 2 polynome :: [ Int ] -> Int 3 length :: [ Char ] -> Int
Interessante Konstanten Im Allgemeinen sind Konstanten monomorph, mit interessanten Ausnahmen: 1 42:: Int, 42:: Integer, 42:: Float, 42:: Double Zahlen gehören mehreren Typen an wird unter Typklassen erklärt
Interessante Konstanten Im Allgemeinen sind Konstanten monomorph, mit interessanten Ausnahmen: 1 42:: Int, 42:: Integer, 42:: Float, 42:: Double Zahlen gehören mehreren Typen an wird unter Typklassen erklärt Was ist der Typ von Nothing in den folgenden Zeilen? 1 if Just " Justine " /= Nothing then... 2 let x = Nothing in...
Typ-Parameter Polymorpher Typ: Typ mit Typ-Parametern 1 x:: Tree a, y:: Maybe a, z ::[ a] 2 length ::[ a] -> Int
Typ-Parameter Polymorpher Typ: Typ mit Typ-Parametern 1 x:: Tree a, y:: Maybe a, z ::[ a] 2 length ::[ a] -> Int Eigentlich ist fast alles polymorph: 1 (++):: [a] -> [a] -> [a] 2 concat :: [[a]] -> [a] 3 map :: (a -> b) -> [a] -> [b] 4 foldr :: (a -> b -> b) -> b -> [a] -> b 5 foldr :: (a -> a -> a) -> a -> [a] -> a Der letzte Typ wäre für foldr zu speziell!
Typ-Konstruktoren Typen werden durch Typ-Ausdrücke beschrieben, bestehend aus Typ-Konstruktoren + Typ-Parametern 1 [a] -- Listen 2 a - > b -- Funktionen 3 (a,b) -- Paare, Tupel 4 Maybe a -- Maybe 5 Tree a -- Baeume Wie im Fall von Tree a können wir beliebige Datentypen definieren und damit selbst neue Typ-Konstruktoren einführen.
Hierarchie polymorpher Tpen Typen werden spezieller, wenn man für die Typ-Variablen Typ-Ausdrücke einsetzt. Der allgemeinste Typ ist einfach 1 a
Hierarchie polymorpher Tpen Typen werden spezieller, wenn man für die Typ-Variablen Typ-Ausdrücke einsetzt. Der allgemeinste Typ ist einfach 1 a Wir könennen ihn spezialisieren zu 1 Bool String Integer Double oder auch wieder polymorph zu 1 [a] a -> b Maybe a Tree a und wieder weiter zu... (Tafel) Gibt es zu jedem Typ auch eine Funktion, die diesen Typ hat?
Typ-Spezialisierung In der Anwendung einer polymorphen Funktion spezialisiert sich ihr Typ: 1 length " abraham " 2 length :: String - > Int 3 4 length [" abraham ", " bebraham ", " cebraham "] 5 length :: [ String ] -> Int 6 7 a :" braham " 8 (:) :: Char -> [ Char ] -> Char Mit jedem Argument wird der Typ spezieller...
Typ-Inferenz Der allgemeinste Typ jeden Ausdrucks, jeder Anwendung, jeder Variable läßt sich automatisch bestimmen. Konsequenz: Typ-Deklarationen sind nicht notwendig, sondern optional Der allgemeinste Typ wird immer berechnet Allgemeinster Typ > deklarierter Typ: Vom Programmierer gewollte Typ-Einschränkung Allgemeinster Typ < deklarierter Typ: Fehler!! Obwohl optional, gibt man Typ-Deklarationen meistens an (Kontrolle, Dokumentation)
Typ-Inferenz am Beispiel Aus den definierenden Gleichungen einer Funktion für diese Funktion: 1 map f [] = [] 2 map f ( x: xs) = f x : map f xs Dabei ist der Typ von (:) bekannt.
Typ-Inferenz am Beispiel Aus den definierenden Gleichungen einer Funktion für diese Funktion: 1 map f [] = [] 2 map f ( x: xs) = f x : map f xs Dabei ist der Typ von (:) bekannt. Aus bekannten Typen der Variablen der Typ eines Ausdrucks: 1 let f = map ( u :) in... Hier sind die Typen von (:), ( u :) und map ( u :) zu bestimmen, während der allgemeinste Typ von (:) und map gegeben ist.
Typ-Polymorphismus mit Typ-Klassen Aus unseren Beispielen kennen wir schon die Typ-Kontexte, die Typ-Parameter einschränken: Allgemeine Form von Typ-Ausdrücken 1 expr :: type 2 expr :: typecontext = > type
Type signatures Allgemeine Form von Typ-Deklarationen 1 vars :: type 2 vars :: typecontext = > type mit vars = var 1,..., var n, n > 0
Type signatures Allgemeine Form von Typ-Deklarationen 1 vars :: type 2 vars :: typecontext = > type mit vars = var 1,..., var n, n > 0 var i müssen im selben scope definiert sein
Type signatures Allgemeine Form von Typ-Deklarationen 1 vars :: type 2 vars :: typecontext = > type mit vars = var 1,..., var n, n > 0 var i müssen im selben scope definiert sein Example 1 sort :: ( Ord a) => [a] -> [a] Der Typcontext schränkt den Typ-Parameter a ein
Typklassen Haskell-Klassen: In Haskell sind Typen in Klassen organisiert Jede Typklasse ist durch eine Menge von (nur) deklarierten Funktionen charakterisiert Die Klassenfunktionen werden auch Methoden genannt Typen können Mitglieder, d.h. Instanzen von Klassen werden Instanzen müssen die Methoden der Klasse implementieren Klassen können Default-Implementierungen enthalten
Typklassen Beispiele für Typklassen Eq Überprüfung auf Gleichheit. In der Klasse Eq sind die Operationen == und /= definiert. Instanzen z.b.: Int, Float, Double, String
Typklassen Beispiele für Typklassen Eq Überprüfung auf Gleichheit. In der Klasse Eq sind die Operationen == und /= definiert. Instanzen z.b.: Int, Float, Double, String Ord Ordnungsrelation. In der Klasse Ord sind die Operationen <,>,<=,>= definiert. Instanzen z.b.: Int, Float, Double, String
Typklassen Beispiele für Typklassen Eq Überprüfung auf Gleichheit. In der Klasse Eq sind die Operationen == und /= definiert. Instanzen z.b.: Int, Float, Double, String Ord Ordnungsrelation. In der Klasse Ord sind die Operationen <,>,<=,>= definiert. Instanzen z.b.: Int, Float, Double, String Num Umfasst die numerischen Typen, z.b. Int, Float, Double Operationen: +,-,*
Typklassen Beispiele für Typklassen Eq Überprüfung auf Gleichheit. In der Klasse Eq sind die Operationen == und /= definiert. Instanzen z.b.: Int, Float, Double, String Ord Ordnungsrelation. In der Klasse Ord sind die Operationen <,>,<=,>= definiert. Instanzen z.b.: Int, Float, Double, String Num Umfasst die numerischen Typen, z.b. Int, Float, Double Operationen: +,-,* Show Werte eines Typs, der Instanz der Klasse Show ist, lassen sich ausgeben mit show x instanzen z.b.: Int, Float, Double, String
Vordefinierte Typklassen Quelle: http:// commons. wikimedia. org/wiki/ File: Classes. png, modifizierte Version aus dem Haskell 98 Report
Einbau in eine Klassenhierarchie Eigenschaften der Klassenhierarchie Klassen-Erweiterung ist additiv - Methoden kommen hinzu. Erweiterung der Typklasse verkleinert die Menge der Instanzen. Beispiel: Komplexe Zahlen - wo würde man sie in die vordefinierte Hierarchie einbauen?
Definition 1 class Classname Typevar where 2 decl1 :: type11 ->... -> type1r_1 3... 4 decln :: typen1 ->... -> type1r_n 5 6 def ault_f unc_de f1 7 def ault_f unc_de f2 8...
Definition 1 class Classname Typevar where 2 decl1 :: type11 ->... -> type1r_1 3... 4 decln :: typen1 ->... -> type1r_n 5 6 def ault_f unc_de f1 7 def ault_f unc_de f2 8... 9 class Su pe rc la ss_c on te xt = > Classname Typevar where 10 decl1 :: type11 ->... -> type1r_1 11... 12 decln :: typen1 ->... -> typenr_n 13 14 def ault_f unc_de f1 15 def ault_f unc_de f2 16...
Beispiel: EQ 1 class Eq a where 2 (==), (/=) :: a -> a -> Bool 3 4 -- Minimal complete definition : ( ==) or (/ =) 5 x == y = not (x/=y) 6 x /= y = not (x==y) Quelle: /usr/lib/hugs/packages/hugsbase/hugs/prelude.hs In Worten: Ein Typ a ist Instanz der Klasse Eq, wenn auf ihm die Operationen == und /= implementiert sind.
Beispiel: Ord 1 c l a s s (Eq a ) => Ord a where 2 compare : : a > a > Ordering 3 ( <), (<=), (>=), (>) : : a > a > Bool 4 max, min : : a > a > a 5 6 Minimal complete d e f i n i t i o n : (<=) or compare 7 u s i n g compare can be more e f f i c i e n t f o r complex t y p e s 8 compare x y x==y = EQ 9 x<=y = LT 10 otherwise = GT 11 12 x <= y = compare x y /= GT 13 x < y = compare x y == LT 14 x >= y = compare x y /= LT 15 x > y = compare x y == GT 16 17 max x y x <= y = y 18 otherwise = x 19 min x y x <= y = x 20 otherwise = y
Beispiel: Ord In Worten: Ein Typ a ist Instanz der Klasse Instanz der Klasse Eq ist, und auf ihm die Operationen <, >, <=, >= und max, min, compare implementiert sind. Ord, wenn er
Rolle der Defaults In der Klassendefinition werden Default -Gleichungen für die Klassen-Methoden angegeben Die Methoden können einander gegenseitig definieren,...... die Instanz muss dann nur einen Teil implementieren Die Gleichungen sollten von den Implementierungen erfüllt werden,...... was aber nicht geprüft werden kann
Beispiel: Enum 1 c l a s s Enum a where 2 succ, pred : : a > a 3 toenum : : I n t > a 4 fromenum : : a > I n t 5 enumfrom : : a > [ a ] [ n.. ] 6 enumfromthen : : a > a > [ a ] [ n,m.. ] 7 enumfromto : : a > a > [ a ] [ n..m] 8 enumfromthento : : a > a > a > [ a ] [ n, n.. m] 9 10 Minimal complete d e f i n i t i o n : toenum, fromenum 11 succ = toenum. (1+). fromenum 12 pred = toenum. s u b t r a c t 1. fromenum 13 enumfrom x = map toenum [ fromenum x.. ] 14 enumfromto x y = map toenum [ fromenum x.. fromenum 15 enumfromthen x y = map toenum [ fromenum x, fromenum y 16 enumfromthento x y z = map toenum [ fromenum x, fromenum y Quelle: /usr/lib/hugs/packages/hugsbase/hugs/prelude.hs
Weitere Typklassen Vordefiniert: Es gibt viele weitere vordefinierte Typklassen Hugs gibt Auskunft per :info XX über Methoden von XX... und Instanzen (!)...... aber nicht über Defaults zum Beispiel: Eq, Show, Ix,... Selbstdefiniert: Eigene Typklassen durch neue Klassendefinitionen Eigene Instanzen durch Instanz-Deklarationen Beispiel: class (Eq a) => Group a where...
Instanzen 1 instance Classname Type where 2 def1 3... 4 defn
Instanzen 1 instance Classname Type where 2 def1 3... 4 defn 5 instance Context = > Classname Type where 6 def1 7... 8 defn Typ wird zur Instanz der Klasse erklärt (explizit!)
Beispiel (Prelude) 1 instance Eq Char where 2 c == c = fromenum c == fromenum c 3 4 instance Ord Char where 5 c <= c = fromenum c <= fromenum c Quelle: Haskell Report Hier wird benutzt, dass die anderen Funktionen der Typklasse über Defaults definiert sind.
Derived Instances Bei der Deklaration von einem Algebraischen Datentyp können mit deriving Instanzen automatisch erzeugt werden: Definition data T a 1... a m = C 1 t 11... t 1n1... C r t r1... t rnr deriving (k 1,..., k u )
Derived Instances Bei der Deklaration von einem Algebraischen Datentyp können mit deriving Instanzen automatisch erzeugt werden: Definition data T a 1... a m = C 1 t 11... t 1n1... C r t r1... t rnr deriving (k 1,..., k u ) aus der Prelude geht das für: Eq, Ord, Enum, Show,...
Derived Instances wenn eine Typklasse eine Superklasse hat, muss der Datentyp schon Mitglied dieser Instanz sein
Derived Instances wenn eine Typklasse eine Superklasse hat, muss der Datentyp schon Mitglied dieser Instanz sein aber die automatische Instanziierung erfüllt nicht immer DWIM
Beispiel data Temperatur = Temp Float Einheit deriving ( Eq, Show )
Beispiel data Temperatur = Temp Float Einheit deriving ( Eq, Show ) Wegen deriving Eq können wir nun automatisch Temperaturen vergleichen: Temp 506 Kelvin == Temp 506 Kelvin -- = > True
Beispiel data Temperatur = Temp Float Einheit deriving ( Eq, Show ) Wegen deriving Eq können wir nun automatisch Temperaturen vergleichen: Temp 506 Kelvin == Temp 506 Kelvin -- = > True Aber: Temp 506 Kelvin == conv ( Temp 506 Kelvin ) Fahrenheit -- => False
Wiederholung 1 conv :: Temperatur - > Einheit - > Temperatur 2 conv ( Temp t Celsius ) Kelvin = 3 Temp ( t + 273. 15) Kelvin 4 conv ( Temp t Kelvin ) Fahrenheit = 5 Temp (t *9/5-459.67) Fahrenheit 6...
Eigene Instanz 1 data Temperatur = Temp Float Einheit 2 deriving Show
Eigene Instanz 1 data Temperatur = Temp Float Einheit 2 deriving Show 3 instance Eq Temperatur where 4 ( Temp t Celsius ) == ( Temp u Celsius ) 5 = t == u 6 ( Temp t Fahrenheit ) == ( Temp u Fahrenheit ) 7 = t == u 8 ( Temp t Kelvin ) == ( Temp u Kelvin ) 9 = t == u 10 ( Temp t Kelvin ) == ( Temp u Fahrenheit ) 11 = conv ( Temp t Kelvin ) Fahrenheit 12 == Temp u Fahrenheit und so weiter