Universität Bielefeld Programmieren in Haskell Giegerich Programmieren in Haskell WS 2012/2013 Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Robert Giegerich Universität Bielefeld AG Praktische Informatik January 22, 2014 Das Haskell Typ-System Universität Bielefeld Programmieren in Haskell In allen Programmiersprachen sind die Typ-Konzepte ein prägendes Element. Das Haskell Typ-System hat zwei grundlegende Konzepte: Giegerich Typ-Systeme TypPolymorphismus Parametrischer Typ-Polymorphismus (bekannt) Typ-Klassen Typ-Klassen (teilweise bekannt, heute Neues dazu) Typklassen definieren Andere Konzepte: untypisierte Sprachen monomorphe Typen Overloading Objektklassen und Vererbung Strong typing, static typing Unter “strong typing” (strenge Typisierung) in einer Programmiersprache versteht man: Alle Typen werden durch den Compiler überprüft Typen, die der Compiler nicht prüfen kann, werdem zur Laufzeit des Programms geprüft Ein Programm kann daher keine unbemerkten Fehler machen, die (auch) Typfehler sind Unter “static typing” (statische Typen) versteht man darüberhinaus: Alle möglichen Typfehler können vom Compiler erkannt werden Typ-Sicherheit ist erreichbar ohne Effizienzverlust zur Laufzeit Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Praktische Bedeutung des Typ-Systems Fehler im Programmentwurf kann man grob unterteilen: “Denkfehler”: Der Algorithmus löst das vorgegebene Problem nicht Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme “Codierfehler”: Die algorithmische Idee ist richtig, aber ihre Formulierung im Programm ist fehlerhaft TypPolymorphismus Die meisten Codierfehler äußern sich als Typfehler – je vollständiger, desto besser!! Typklassen definieren Typ-Klassen Praktische Bedeutung des Typ-Systems Fehler im Programmentwurf kann man grob unterteilen: “Denkfehler”: Der Algorithmus löst das vorgegebene Problem nicht Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme “Codierfehler”: Die algorithmische Idee ist richtig, aber ihre Formulierung im Programm ist fehlerhaft TypPolymorphismus Die meisten Codierfehler äußern sich als Typfehler – je vollständiger, desto besser!! Typklassen definieren "Die leichte Taube, indem sie im freien Fluge die Luft teilt, deren Widerstand sie fühlt, könnte die Vorstellung fassen, dass es ihr im luftleeren Raum noch viel besser gelingen werde." Immanuel Kant - Kritik der reinen Vernunft [1787] Typ-Klassen Monomorphe Typen Universität Bielefeld Programmieren in Haskell Giegerich 1 2 Monomorpher Typ: vollständig festgelegt, z.B. bei Konstanten Typ-Systeme True , Note ce (1/2) , " abraham " , Just " Justine " TypBool , Musik , [ Char ] Maybe String Polymorphismus Typ-Klassen Typklassen definieren Monomorphe Typen Universität Bielefeld Programmieren in Haskell Giegerich 1 2 Monomorpher Typ: vollständig festgelegt, z.B. bei Konstanten Typ-Systeme True , Note ce (1/2) , " abraham " , Just " Justine " TypBool , Musik , [ Char ] Maybe String Polymorphismus Typ-Klassen Typklassen definieren 1 2 3 Monomorpher Typ: vollständig festgelegt, z.B. durch Deklaration x :: Integer , y :: Bool , 42:: Integer polynome :: [ Int ] -> Int length ’ :: [ Char ] -> Int Monomorphe Typen bieten Typsicherheit, sind aber unflexibel Warum Typ-Polymorphismus? 1 Ziel: Typsicherheit + Wiederverwendbarkeit von Code > length ::[ a ] -> Int Universität Bielefeld Programmieren in Haskell Giegerich 2 3 4 > length [] = 0 > 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. Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Warum Typ-Polymorphismus? 1 Ziel: Typsicherheit + Wiederverwendbarkeit von Code > length ::[ a ] -> Int Universität Bielefeld Programmieren in Haskell Giegerich 2 3 4 1 2 3 4 5 > length [] = 0 > 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. In der Anwendung auf ein Argument konkretisiert sich der Typ: length :: String -> Int length :: [ Int ] -> Int length :: [ Bool ] -> Int length :: [ String ] -> Int length :: [[ String ]] -> Int -- und so weiter Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Typ-Parameter 1 2 3 4 Polymorpher Typ: Typ mit Typ-Parametern x :: Tree a y :: Maybe a z ::[ a ] length ::[ a ] -> Int Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Typ-Parameter 1 2 3 4 Universität Bielefeld Polymorpher Typ: Typ mit Typ-Parametern x :: Tree a y :: Maybe a z ::[ a ] length ::[ a ] -> Int Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren 1 2 3 4 5 6 7 Eigentlich ist fast alles polymorph: (++):: [ a ] -> [ a ] -> [ a ] concat :: [[ a ]] -> [ a ] map :: ( a -> b ) -> [ a ] -> [ b ] foldr :: ( a -> b -> b ) -> b -> [ a ] -> foldr1 :: ( a -> a -> a ) -> a -> [ a ] -> foldTree :: b -> ( a -> b ) ( b -> b -> b ) Tree a Polymorphe Typen haben hohen Dokumentationswert! b a -> -> b Hierarchie polymorpher Tpen Universität Bielefeld Programmieren in Haskell 1 Typen werden spezieller, wenn man für die Typ-Variablen Typ-Ausdrücke einsetzt. Der allgemeinste Typ ist einfach a Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Hierarchie polymorpher Tpen Universität Bielefeld Programmieren in Haskell 1 Typen werden spezieller, wenn man für die Typ-Variablen Typ-Ausdrücke einsetzt. Der allgemeinste Typ ist einfach a Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren 1 1 Wir könennen ihn spezialisieren zu Bool String Integer Double oder auch wieder polymorph zu [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 1 2 In der Anwendung einer polymorphen Funktion spezialisiert sich ihr Typ: length " abraham " length :: String -> Int 3 4 5 8 Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen length [ " abraham " , " bebraham " , " cebraham " ] length :: [ String ] -> Int 6 7 Universität Bielefeld ’a ’: " braham " (:) :: Char -> [ Char ] -> Char Typklassen definieren Typ-Spezialisierung 1 2 In der Anwendung einer polymorphen Funktion spezialisiert sich ihr Typ: length " abraham " length :: String -> Int 3 4 5 Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen length [ " abraham " , " bebraham " , " cebraham " ] length :: [ String ] -> Int Typklassen definieren 6 7 8 1 2 3 ’a ’: " braham " (:) :: Char -> [ Char ] -> Char Was ist der Typ von foldTree in den Anwendungen > foldTree ( Leaf 0) ( Leaf . length ) Br > ( Br ( Leaf " Adam " ) ( Leaf " Eva " )) > \ x -> foldTree ( Leaf 0) ( Leaf . length ) Br x Typ-Inferenz Universität Bielefeld Programmieren in Haskell Typ-Inferenz heißt: Der allgemeinste Typ jeden Ausdrucks, jeder Anwendung, jeder Variablen läßt sich automatisch bestimmen. Konsequenz: Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen 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) Typklassen definieren Typ-Polymorphismus mit Typ-Klassen Universität Bielefeld Programmieren in Haskell Giegerich Typklassen fassen polymorphe Typen zusammen, auf denen ein geeminsamer Satz von Operationen implementiert ist. In Typ-Deklarationen fungieren sie als Typ-Kontexte, die Typ-Parameter einschränken: Allgemeine Form von Typ-Ausdrücken 1 2 expr :: type expr :: typecontext = > type Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Typ-Deklarationen Universität Bielefeld Programmieren in Haskell Allgemeine Form von Typ-Deklarationen 1 2 vars :: type vars :: typecontext = > type mit vars = var1 , . . . , varn , n > 0 Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Typ-Deklarationen Universität Bielefeld Programmieren in Haskell Allgemeine Form von Typ-Deklarationen 1 2 vars :: type vars :: typecontext = > type mit vars = var1 , . . . , varn , n > 0 vari müssen im selben scope definiert sein Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Typ-Deklarationen Universität Bielefeld Programmieren in Haskell Allgemeine Form von Typ-Deklarationen 1 2 vars :: type vars :: typecontext = > type mit vars = var1 , . . . , varn , n > 0 vari müssen im selben scope definiert sein Example 1 sort :: ( Ord a ) = > [ a ] -> [ a ] Der Typcontext schränkt den Typ-Parameter a ein Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Typ-Deklarationen Universität Bielefeld Programmieren in Haskell Allgemeine Form von Typ-Deklarationen 1 2 vars :: type vars :: typecontext = > type mit vars = var1 , . . . , varn , n > 0 vari müssen im selben scope definiert sein Example 1 sort :: ( Ord a ) = > [ a ] -> [ a ] Der Typcontext schränkt den Typ-Parameter a ein NB: Was kann man vom Typ her über die berechnete Funktion erschließen? Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Typklassen Universität Bielefeld Programmieren in Haskell Haskell-Klassen: In Haskell sind Typen in Klassen organisiert Giegerich Typ-Systeme Jede Typklasse ist durch eine Menge von (nur) deklarierten Funktionen charakterisiert TypPolymorphismus Die Klassenfunktionen werden auch Methoden genannt Typklassen definieren Typen können Mitglieder, d.h. Instanzen von Klassen werden Instanzen müssen die Methoden der Klasse implementieren Klassen können Default-Implementierungen enthalten Durch die Defaults sind Eigenschaften der Methoden ausgedrückt Typ-Klassen Typklassen mit syntaktischem Zucker Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme Mit manchen (vordefinierten) Typklassen ist spezielle Notation assoziiert: Enum: Aufzählungen wie in [1, 3 ..], [’A’ .. ’Z’] Monad: Do-Notation für >>= , >> TypPolymorphismus Typ-Klassen Typklassen definieren Unterschied Klassenmethode – polymorphe Funktion Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Unterschied Klassenmethode – polymorphe Funktion Universität Bielefeld Programmieren in Haskell Giegerich Eine polymorphe Funktion ist nur einmal implementiert. Sie hat einen allgemeinsten Typ und ist auf alle Typen anwendbar, die Spezialisierungen davon sind. Typ-Systeme Eine Klassenmethode hat viele Implementierungen, eine für jeden Typ, der Mitglied der Klasse ist. Typklassen definieren Was man damit erreicht, nennt man in anderen Sprachen "Overloading": verschiedene Funktionen stehen hinter dem gleichen Funktionsnamen, je nach Argumenttyp wird eine ausgewählt. TypPolymorphismus Typ-Klassen Vordefinierte Typklassen Universität Bielefeld Programmieren in Haskell Quelle: Giegerich http:// Typ-Systeme commons. TypPolymorphismus wikimedia. Typ-Klassen org/wiki/ File: Classes. png, modifizierte Version aus dem Haskell 98 Report Typklassen definieren Details zu Typklassen Universität Bielefeld Programmieren in Haskell Giegerich Eq Überprüfung auf Gleichheit. In der Klasse Eq sind die Operationen == und /= definiert. Instanzen z.B.: Int, Float, Double, String Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Details zu Typklassen Universität Bielefeld Programmieren in Haskell Giegerich 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 Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Details zu Typklassen Universität Bielefeld Programmieren in Haskell Giegerich 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: +,-,* Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Details zu Typklassen Universität Bielefeld Programmieren in Haskell Giegerich 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 Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Klassenhierarchie Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme Neue Klassen können gegebene Klassen erweitern. 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? TypPolymorphismus Typ-Klassen Typklassen definieren Definition 1 2 3 4 class Classname Typevar where decl1 :: type11 -> ... -> type1r_1 ... decln :: typen1 -> ... -> type1r_n 7 8 Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus 5 6 Universität Bielefeld def ault_func_de f1 def ault_func_de f2 ... Typ-Klassen Typklassen definieren Definition 1 2 3 4 class Classname Typevar where decl1 :: type11 -> ... -> type1r_1 ... decln :: typen1 -> ... -> type1r_n 7 8 9 10 11 12 15 16 Giegerich Typ-Systeme Typ-Klassen def ault_func_de f1 Typklassen def ault_func_de f2 definieren ... class Superclass_C on te xt = > Classname Typevar where decl1 :: type11 -> ... -> type1r_1 ... decln :: typen1 -> ... -> typenr_n 13 14 Programmieren in Haskell TypPolymorphismus 5 6 Universität Bielefeld def ault_func_de f1 def ault_func_de f2 ... Beispiel: EQ Universität Bielefeld Programmieren in Haskell Giegerich 1 2 In Worten: Ein Typ a ist Instanz der Klasse Eq, wenn auf ihm die Operationen == und /= implementiert sind. class Eq a where (==) , (/=) :: a -> a -> Bool Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren 3 4 5 6 -- Minimal complete definition : (==) or (/=) x == y = not ( x /= y ) x /= y = not ( x == y ) Quelle: /usr/lib/hugs/packages/hugsbase/Hugs/Prelude.hs Beispiel: Ord (eine Erweiterung von Eq) 1 2 3 4 Universität Bielefeld c l a s s ( Eq a ) => Ord a where compare : : a −> a −> O r d e r i n g ( <) , ( <=) , ( >=) , (>) : : a −> a −> Bool max , min : : a −> a −> a Programmieren in Haskell Giegerich Typ-Systeme 5 6 7 8 9 10 Typ−− M i n i m a l c o m p l e t e d e f i n i t i o n : (<=) o r compare Polymorphismus −− u s i n g compare can be more e f f i c i e n t f o r c o m p l e x tTyp-Klassen ypes compare x y | x==y = EQ Typklassen | x<=y = LT definieren | o t h e r w i s e = GT 11 12 13 14 15 x x x x <= < >= > y y y y = = = = compare compare compare compare = = = = y x x y 16 17 max x y 18 19 20 min x y | | | | x <= y otherwise x <= y otherwise x x x x y y y y /= == /= == GT LT LT GT Beispiel: Ord Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme In Worten: Ein Typ a ist Instanz der Klasse Ord, wenn er Instanz der Klasse Eq ist, und auf ihm die weiteren Operationen <, >, <=, >= und max, min, compare implementiert sind. TypPolymorphismus Typ-Klassen Typklassen definieren Rolle der Defaults Universität Bielefeld Programmieren in Haskell Giegerich 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 Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Beispiel: Monad Universität Bielefeld Programmieren in Haskell Giegerich Vordefinierte Typklasse: 1 2 3 4 (Achtung: m ist eine Variable für einen einstelligen Typ-Konstruktor!) Typ-Systeme infixl 1 > >= , >> class Monad m where ( > >=) :: m a -> ( a -> m b ) -> m b return :: a -> m a TypPolymorphismus 5 6 7 ( > >) fail :: m a -> m b :: String -> m a -> m b Typ-Klassen Typklassen definieren Beispiel: Monad Universität Bielefeld Programmieren in Haskell Giegerich Vordefinierte Typklasse: 1 2 3 4 (Achtung: m ist eine Variable für einen einstelligen Typ-Konstruktor!) Typ-Systeme infixl 1 > >= , >> class Monad m where ( > >=) :: m a -> ( a -> m b ) -> m b return :: a -> m a TypPolymorphismus 5 6 7 ( > >) fail :: m a -> m b :: String -> m a -> m b 6 7 8 m >> k fail s = m > >= \ _ -> k = error s -- default -- default Typ-Klassen Typklassen definieren Monad-Laws Universität Bielefeld Programmieren in Haskell Giegerich 1 return gibt nur sein Argument zurück und führt keine weiteren Berechnungen durch: m >>= return = m return x >>= f = f x 2 >>= ist assoziativ: (m >>= f) >>= g = m >>= (\x -> (f x >>= g)) analog zur Komposition von Funktionen (m . f ) . g = m . (f . g) Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Monad-Laws Universität Bielefeld Programmieren in Haskell Giegerich 1 return gibt nur sein Argument zurück und führt keine weiteren Berechnungen durch: m >>= return = m return x >>= f = f x 2 >>= ist assoziativ: (m >>= f) >>= g = m >>= (\x -> (f x >>= g)) analog zur Komposition von Funktionen (m . f ) . g = m . (f . g) Dies sind allgemeine Gesetze für Monaden; sie können nicht als Defaults definiert werden. Sie werden ebensowenig überprüft wie die Defaults. Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Beispiel: Enum 1 2 3 4 5 6 7 8 c l a s s Enum a where succ , pred toEnum fromEnum enumFrom enumFromThen enumFromTo enumFromThenTo Universität Bielefeld Programmieren in Haskell :: :: :: :: :: :: :: a −> a I n t −> a a −> I n t a −> [ a ] a −> a −> [ a ] a −> a −> [ a ] a −> a −> a −> [ a ] Giegerich Typ-Systeme −− −− −− −− Typ- [ n . . ] Polymorphismus [ n ,m . .Typ-Klassen ] [ n . . m] Typklassen [ n , n ’ .definieren . m] 9 10 11 12 13 14 15 16 −− M i n i m a l c o m p l e t e d e f i n i t i o n : toEnum , fromEnum succ = toEnum . (1+) . fromEnum pred = toEnum . s u b t r a c t 1 . fromEnum enumFrom x = map toEnum [ fromEnum x . . ] enumFromTo x y = map toEnum [ fromEnum x . . fromEnum enumFromThen x y = map toEnum [ fromEnum x , fromEnum y enumFromThenTo x y z = map toEnum [ fromEnum x , fromEnum y Quelle: /usr/lib/hugs/packages/hugsbase/Hugs/Prelude.hs Weitere Typklassen Universität Bielefeld Programmieren in Haskell Vordefiniert: Giegerich Es gibt viele weitere vordefinierte Typklassen Typ-Systeme Hugs gibt Auskunft per :info XX über Methoden von XX TypPolymorphismus ... und Instanzen (!) ... Typ-Klassen ... aber nicht über Defaults Typklassen definieren zum Beispiel: Eq, Show, Ix, ... Selbstdefiniert: Eigene Typklassen durch neue Klassendefinitionen Eigene Instanzen durch Instanz-Deklarationen Beispiel: class (Eq a) => Group a where ... Vereinigung von Typklassen Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme 1 2 Vereinigung von Typklassen geschieht durch ihre gemeinsame Angabe als Typkontext: > class ( Real a , Fractional a ) = > RealFrac a > where { } Damit hat die Klasse RealFrac alle Methoden von Real und Fractional (und keine neuen). Das wahre RealFrac fügt noch eigene Methoden hinzu. TypPolymorphismus Typ-Klassen Typklassen definieren Instanzen Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 4 instance Classname Type where def1 ... defn Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Instanzen Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 4 5 6 7 8 instance Classname Type where def1 ... defn instance Context = > Classname Type where def1 ... defn Typ wird zur Instanz der Klasse erklärt (explizit!) Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Beispiel (Prelude) Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme 1 2 instance Eq Char where c == c ’ = fromEnum c == fromEnum c ’ Typ-Klassen 3 4 5 TypPolymorphismus instance Ord Char where c <= c ’ = fromEnum c <= fromEnum c ’ Quelle: Haskell Report Hier wird benutzt, dass die anderen Funktionen der Typklasse über Defaults definiert sind. Typklassen definieren Listen-Beispiel 1 2 -- data [ a ] = a : [ a ] | -[] 5 6 Programmieren in Haskell Giegerich Typ-Systeme 3 4 Universität Bielefeld instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] Die anderen Monaden-Funktionen haben Defaults TypPolymorphismus Typ-Klassen Typklassen definieren Listen-Beispiel 1 2 -- data [ a ] = a : [ a ] | -[] 5 6 Programmieren in Haskell Giegerich Typ-Systeme 3 4 Universität Bielefeld instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] Die anderen Monaden-Funktionen haben Defaults do x <- [1 ,2] -- ein Fall von >> y <- [3 ,4] -- ein Fall von >> return ( x * y ) TypPolymorphismus Typ-Klassen Typklassen definieren Listen-Beispiel 1 2 -- data [ a ] = a : [ a ] | -[] 5 6 Programmieren in Haskell Giegerich Typ-Systeme 3 4 Universität Bielefeld instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] Die anderen Monaden-Funktionen haben Defaults do x <- [1 ,2] -- ein Fall von >> y <- [3 ,4] -- ein Fall von >> return ( x * y ) = > [3 ,4 ,6 ,8] TypPolymorphismus Typ-Klassen Typklassen definieren Listen-Beispiel 1 2 -- data [ a ] = a : [ a ] | -[] 5 6 Programmieren in Haskell Giegerich Typ-Systeme 3 4 Universität Bielefeld instance Monad [] where ( > >=) l f = concat ( map f l ) return x = [x] Die anderen Monaden-Funktionen haben Defaults do x <- [1 ,2] -- ein Fall von >> y <- [3 ,4] -- ein Fall von >> return ( x * y ) = > [3 ,4 ,6 ,8] Analog zu folgender Listenbeschreibung: [ x * y | x <- [1 ,2] , y <- [3 ,4] ] TypPolymorphismus Typ-Klassen Typklassen definieren Derived Instances Universität Bielefeld Bei der Deklaration von einem Algebraischen Datentyp können mit deriving Instanzen automatisch erzeugt werden: Programmieren in Haskell Giegerich Typ-Systeme Definition TypPolymorphismus Typ-Klassen data T a1 . . . am = C1 t11 . . . t1n1 ... Cr tr 1 . . . trnr deriving (k1 , . . . , ku ) | | Typklassen definieren Derived Instances Universität Bielefeld Bei der Deklaration von einem Algebraischen Datentyp können mit deriving Instanzen automatisch erzeugt werden: Programmieren in Haskell Giegerich Typ-Systeme Definition TypPolymorphismus Typ-Klassen data T a1 . . . am = C1 t11 . . . t1n1 ... Cr tr 1 . . . trnr deriving (k1 , . . . , ku ) aus dem Prelude geht das für: Eq, Ord, Enum, Show, . . . | | Typklassen definieren Derived Instances Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus wenn eine Typklasse eine Superklasse hat, muss der Datentyp schon Mitglied dieser Instanz sein Typ-Klassen Typklassen definieren Derived Instances Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme TypPolymorphismus wenn eine Typklasse eine Superklasse hat, muss der Datentyp schon Mitglied dieser Instanz sein die automatische Instanziierung mit deriving bringt nicht immer, was man haben will Typ-Klassen Typklassen definieren Beispiel: Rationale Zahlen Universität Bielefeld Programmieren in Haskell Giegerich 1 > data Rat = Rat Int Int Typ-Systeme Der Bruch 67/18 wird durch Rat 67 18 dargestellt. Die gleiche Zahl hat viele verschiedene Darstellungen: z.B. 67/18 = 8241/2214. TypPolymorphismus Typ-Klassen Typklassen definieren Beispiel: Rationale Zahlen Universität Bielefeld Programmieren in Haskell Giegerich 1 > data Rat = Rat Int Int Typ-Systeme Der Bruch 67/18 wird durch Rat 67 18 dargestellt. Die gleiche Zahl hat viele verschiedene Darstellungen: z.B. 67/18 = 8241/2214. 1 2 1 TypPolymorphismus Typ-Klassen Typklassen definieren Daher ist die folgende Definition von (==) auf Rat falsch (diese Definition würde mit deriving (Eq) erzeugt werden): > instance Eq ( Rat ) where > Rat x y == Rat x ’ y ’ = x == x ’ && y == y ’ Richtige Definition: > Rat x y == Rat x ’ y ’ = x *y ’ == x ’* y Rat-Zahlen als Instanz von Eq Universität Bielefeld Programmieren in Haskell 1 2 1 2 3 4 1 > instance Eq Rat where Giegerich > Rat x y == Rat x ’ y ’ = x *y ’ == x ,* y Typ-Systeme Unschön. Bei jedem Gleichheitstest werden zwei TypPolymorphismus Multiplikationen durchgeführt. Typ-Klassen Ausweg: “smarter Konstruktor” Typklassen > rat :: Int -> Int -> Rat definieren > rat x y = norm ( x * signum y ) ( abs y ) where > norm x y = Rat ( x ‘div ‘ d ) ( y ‘div ‘ d ) where > d = gcd x y Wenn wir davon ausgehen, daß Elemente vom Typ Rat immer normiert sind, dann kann doch die Standarddefinition von == verwendet werden. > Rat x y == Rat x ’ y ’ = x == x ’ && y == y ’ Konsequenzen Universität Bielefeld Programmieren in Haskell Design-Entscheidung: Giegerich Die erste Lösung erfordert zwei Multiplikationen pro Vergleich Typ-Systeme Bei der Lösung mit smart Konstruktor und effizientem Vergleich muss nach jeder Operation auf rationalen Zahlen normalisiert werden. Typ-Klassen Dies ist Mehraufwand, spart aber ggf. Platz, weil Zähler und Nenner weniger stark wachsen Rat als ADT erlaubt das Experimentieren mit beiden Lösungen. Der ADT für die smart-Konstruktor-Lösung würde nicht Rat, sondern nur rat exportieren. TypPolymorphismus Typklassen definieren Erweiterte Zahltypen: Inf n Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme Typ- 1 1 Polymorphismus Weiteres Beispiel: Typ-Klassen Zahltyp Inf n erweitert Zahltyp n um +∞ und −∞ > data Inf n = Fin n | Inf Bool deriving ( Eq )Typklassen definieren Danach integriert in Ord, Show, Num. > instance ( Ord n ) = > Ord ( Inf n ) where ... Siehe Skript. Typklasse Sequenzen Universität Bielefeld Programmieren in Haskell Giegerich Sequentielle Daten kann man unterschiedlich darstellen als Listen als HolyLists als Bäume als Arrays und als alle die obigen mit Zusatz-Informationen Auf allen Sequenz-Typen will man einen gemeinsamen Satz von Operatioenn haben, ggf. je nach Darstellung mit unterschiedlicher Effizienz Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Deklaration der Methoden Universität Bielefeld Programmieren in Haskell 1 2 3 4 5 6 7 8 9 10 11 12 > class Sequence s where > empty :: s a > single :: a -> s a > isEmpty , isSingle :: s a -> Bool > ( <|) :: a -> s a -> s a > (| >) :: s a -> a -> s a > hd :: s a -> a > tl :: s a -> s a > ( < >) :: s a -> s a -> s a > len :: s a -> Int > fromList :: [ a ] -> s a > toList :: s a -> [ a ] Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Default-Definitionen Universität Bielefeld Programmieren in Haskell 1 2 3 4 5 6 7 8 9 > > > > > > > > > single a isSingle a <| s = s |> a = x <> y = len = inc x fromList toList Giegerich = a <| empty s = not ( isEmpty s ) && isEmpty Typ-Systeme ( tl s ) Typsingle a <> s Polymorphismus s <> single a Typ-Klassen foldrS ( <|) y x Typklassen definieren foldrS inc 0 where y = y +1 = foldr ( <|) empty = foldrS (:) [] keine Defaults für empty, isEmpty, hd, tl zirkuläre Defaults für <|, |>, <> Abgeleitete Funktionen Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 > > > Typ-Systeme foldrS (*) e s Typ| isEmpty s = e Polymorphismus | otherwise = hd s * foldrS (*) e ( tl Typ-Klassen s) Typklassen definieren Beachte: So kann man fold-Operationen definieren, wenn die Konstruktoren des Datentyps gar nicht bekannt sind ... Abgeleitete Funktionen Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 > > > Typ-Systeme foldrS (*) e s Typ| isEmpty s = e Polymorphismus | otherwise = hd s * foldrS (*) e ( tl Typ-Klassen s) Typklassen definieren Beachte: So kann man fold-Operationen definieren, wenn die Konstruktoren des Datentyps gar nicht bekannt sind ... ... weil man die Selektoren (hier: hd, tl) benutzt. Aber Vorsicht: für sie gibt es keine Defaults. Listen als Instanz von Sequence Universität Bielefeld Programmieren in Haskell Giegerich 1 2 3 4 5 6 7 8 > instance Sequence [] where > empty = [] > isEmpty = null > ( <|) = (:) > hd = head > tl = tail > toList = id > fromList = id Prüfe: Ist hier wirklich alles definiert? Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Bäume als Instanz von Sequence Universität Bielefeld Programmieren in Haskell 1 2 3 4 5 6 Ein Fehlstart: > instance Sequence Tree where > empty = Nil > isEmpty Nil = True > isEmpty t = False > ( < >) = Br > etc . Das erfüllt die Anforderungen, nicht denn wir erhalten isEmpty (empty <> enpty) = False Lösungsmöglichkeiten smarte Konstruktoren aufmerksame Funktionen Die folgende Lösung ist eine Mischung daraus. Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Bäume als Instanz von Sequence Universität Bielefeld Programmieren in Haskell 1 2 3 4 5 6 7 8 9 10 11 > instance Sequence Tree where > empty = Nil > isEmpty Nil = True > isEmpty ( Leaf _ ) = False > isEmpty ( Br l r ) = > isEmpty l && isEmpty r -- aufmerksam > single = Leaf > isSingle Nil = False > isSingle ( Leaf _ ) = True > isSingle ( Br l r ) = > isSingle l /= isSingle r -- aufmerksam isEmpty, isSingle achten darauf, was ggf. die Unterbäume enthalten Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Bäume als Instanz von Sequence Universität Bielefeld Programmieren in Haskell Giegerich 12 13 14 15 16 17 18 19 > > > > > > > > hd ( Leaf a ) = a hd ( Br l r ) | isEmpty l = hd r | otherwise = hd l tl ( Br Nil r ) = tl r tl ( Br ( Leaf _ ) r ) = r tl ( Br ( Br l ’ r ’) r ) = tl ( Br l ’ ( Br r ’ r )) Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren -- Rechtsrotation Das Ergebnis von tl ist immer ein rechtsgekämmter Baum Bäume als Instanz von Sequence Universität Bielefeld Programmieren in Haskell Giegerich 20 21 22 23 24 25 26 27 > > > > > > > > ( < >) = br where -- smart Konstruktor br Nil r = r br l Nil = l br l r = Br l r toList = leaves fromList [] = Nil fromList [ a ] = Leaf a fromList ( a : as ) = a <| fromList as Der smart Konstruktor versucht, den aufmerksamen Funktionen möglichst viel Arbeit zu ersparen. Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Generische Erweiterung Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme Zum Abschluss noch eine beherzte Operation: Wir erweiteren – gewissermaßen im Voraus – alle Sequenz-Typen so, das die Sequenzlänge in O(1) berechnet werden kann. Dir Grundidee ist schon bekannt von Listen: Man verwendet Paare (s, l) mit l = length(s). TypPolymorphismus Typ-Klassen Typklassen definieren Typkonstruktor WithLen Universität Bielefeld Programmieren in Haskell Giegerich Typ-Systeme 1 > data WithLen s a = Len ( s a ) Int Die Variable s steht für einen (einstelligen) Typkonstruktor! Daher kann man rechts auch (s a) schreiben. Wir machen nun ALLE mit WithLen erweiterten Sequenz-Typen zu Sequenzen TypPolymorphismus Typ-Klassen Typklassen definieren WithLen s als Instanz von Sequence Universität Bielefeld Programmieren in Haskell 3 4 5 6 7 8 9 > instance ( Sequence s ) = > Sequence ( WithLen sGiegerich ) whe > empty = Len empty 0 Typ-Systeme > single a = Len ( single a ) 1 TypPolymorphismus > isEmpty ( Len _ n ) = n == 0 Typ-Klassen > isSingle ( Len _ n ) = n == 1 Typklassen definieren > a <| Len s n = Len ( a <| s ) ( n +1) > Len s n | > a = Len ( s | > a ) ( n +1) Diese Instanz ergänzt alle Sequenz-Typen um die Manipulation des Längenwertes Die Klassenmethoden werden für WithLen s definiert ... ... und benutzen dabei die gleichen Methoden auf s. WithLen s als Instanz von Sequence Universität Bielefeld Programmieren in Haskell 11 12 13 14 15 16 > > > > > > hd ( Len s _ ) = hd s tl ( Len s n ) = Len ( tl s ) (n -1) Len s m <> Len t n = Len ( s <> t ) ( m + n ) len ( Len _ n ) = n fromList x = Len ( fromList x ) ( length x ) toList ( Len s _ ) = toList s len benutzt nun natürlich NICHT das len auf dem Typ s, sondern die explizite Längeninformation (len) arbeitet nun in O(1) Im Ergebnis ist damit für JEDEN Sequenztyp s, den wir einführen, automatisch ein Sequenztyp WithLen s verfügbar Giegerich Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren Fazit zum Haskell Typsystem Universität Bielefeld Programmieren in Haskell Giegerich Zusammenfassung Polymorphe Typen erlauben polymorphe Funktionen, Typsicherheit und hohe Wiederverwendbarkeit Typklassen erlauben gleichartige, aber verschieden implementierte Funktion auf unterschiedlichen Typen ... ... mit Eigenschaften, die man schon im Voraus definieren kann. Typ-Systeme TypPolymorphismus Typ-Klassen Typklassen definieren