Module Typklassen Übersicht Einführung in die Funktionale Programmierung: Haskell (2) Dr. David Sabel 1 Haskells hierachisches Modulsystem 2 Typklassen Klassen und Instanzen Konstruktorklassen Auflösung der Überladung Erweiterung von Typklassen WS 2010/11 Stand der Folien: 20. Dezember 2010 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 1 Module Typklassen 2/59 Module Typklassen Haskells hierachisches Modulsystem Moduldefinition in Haskell module Modulname(Exportliste) where Modulimporte, Datentypdefinitionen, M odulrumpf Funktionsdefinitionen, . . . Aufgaben von Modulen Strukturierung / Hierarchisierung Name des Moduls muss mit Großbuchstaben beginnen Kapselung Exportliste enthält die nach außen sichtbaren Funktionen und Datentypen Wiederverwendbarkeit Dateiname = Modulname.hs (bei hierachischen Modulen Pfad+Dateiname = Modulname.hs) Ausgezeichnetes Modul Main muss Funktion main :: IO () exportieren EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 3/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 4/59 Module Typklassen Module Typklassen Import / Export Exporte Beispiel: module Spiel where data Ergebnis = Sieg | Niederlage | Unentschieden berechneErgebnis a b = if a > b then Sieg else if a < b then Niederlage else Unentschieden istSieg Sieg = True istSieg _ = False istNiederlage Niederlage = True istNiederlage _ = False Exportliste kann enthalten Funktionsname (in Präfix-Schreibweise) Z.B.: ausschließlich berechneErgebnis wird exportiert module Spiel(berechneErgebnis) where Datentypen mittels data oder newtype, drei Möglichkeiten Nur Ergebnis in die Exportliste: module Spiel(Ergebnis) where Typ Ergebnis wird exportiert, die Datenkonstruktoren, d.h. Sieg, Niederlage, Unentschieden jedoch nicht module Spiel(Ergebnis(Sieg, Niederlage)) Typ Ergebnis und die Konstruktoren Sieg und Niederlage werden exportiert, nicht jedoch Unentschieden. Durch den Eintrag Ergebnis(..), wird der Typ mit sämtlichen Konstruktoren exportiert. Da keine Exportliste: Es werden alle Funktionen und Datentypen exportiert Außer importierte EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 5/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen Module Typklassen Exporte (2) Importe Exportliste kann enthalten Typsynonyme, die mit type definiert wurden Einfachste Form: Importiert alle Einträge der Exportliste import Modulname module Spiel(Result) where ... wie vorher ... type Result = Ergebnis Andere Möglichkeiten: Explizites Auflisten der zu importierenden Einträge: Importierte Module: module Game where import Spiel(berechneErgebnis, Ergebnis(..)) ... module Game(module Spiel, Result) where import Spiel type Result = Ergebnis Explizites Ausschließen einzelner Einträge: Game exportiert alle Funktionen, Datentypen und Konstruktoren, die auch Spiel exportiert sowie zusätzlich noch den Typ Result. EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 6/59 module Game where import Spiel hiding(istSieg,istNiederlage) ... 7/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 8/59 Module Typklassen Module Typklassen Importe (2) Importe (3) So importierte Funktionen und Datentypen sind mit ihrem unqualifizierten und ihrem qualifizierten Namen ansprechbar. Qualifizierter Name: Modulname.unqualifizierter Name Mit qualifizierten Namen, funktioniert es: module C where import A import B g = A.f 1 2 + B.f 3 4 module A(f) where f a b = a + b module B(f) where f a b = a * b Mit Schlüsselwort qualified kann man nur die qualifizierten Namen importieren: module C where import A import B g = f 1 2 + f 3 4 -- funktioniert nicht! module C where import qualified A g = f 1 2 -- f ist nicht sichtbar Prelude> :l C.hs Prelude> :l C.hs ERROR C.hs:3 - Undefined variable "f" ERROR C.hs:4 - Ambiguous variable occurrence "f" *** Could refer to: B.f A.f EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 9/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 10/59 Module Typklassen Importe (4) Importe (5) Übersicht: Import-Deklaration import M import M() import M(f) import qualified M import qualified M() import qualified M(f) import M hiding () import M hiding (f) import qualified M hiding () import qualified M hiding (f) import M as N import M as N(f) import qualified M as N Lokale Aliase: Schlüsselwort as: import LangerModulName as C EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 11/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel definierte Namen f, g, M.f, M.g keine f, M.f M.f, M.g keine M.f f, g, M.f, M.g g, M.g M.f, M.g M.g f, g, N.f, N.g f, N.f N.f, N.g 12/59 Module Typklassen Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Hierarchische Modulstruktur Modulnamen mit Punkten versehen geben Hierachie an: z.B. module A.B.C Haskells Typklassensystem Allerdings ist das rein syntaktisch (es gibt keine Beziehung zwischen Modul A.B und A.B.C) Beim Import: import A.B.C sucht der Compiler nach dem File namens C.hs im Pfad A/B/ EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 13/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Polymorphismus (1) Module Typklassen 14/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Polymorphismus (2) Ad hoc Polymorphismus: Parametrischer Polymorphismus: Eine Funktion (bzw. Funktionsname) wird mehrfach für verschiedene Datentypen definiert Funktion ist für eine Menge von verschiedenen Typen definiert Verhalten ist für alle Typen gleich Implementierungen für verschiedene Typen können sich unterschiedlich verhalten Implementierung ist unabhängig von den konkreten Typen Beispiele: Ad hoc-Polymorphism nennt man auch Überladung. (++) :: [a] -> [a] -> [a] kann für bel. Listen verwendet werden Beispiele (\x -> x) :: a -> a kann auf beliebiges Argument angewendet werden +, für Integer- und für Double-Werte. map :: (a -> b) -> [a] -> [b] kann für passenden Funktion und Listen verwendet werden map für Listen und Bäume EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel == für Bool und Integer Haskells Typklassen implementieren Ad hoc Polymorphismus 15/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 16/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Typklassen Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Die Klasse Eq In der Klassendefinition wird festgelegt: Typ der Klassenfunktionen class Optional: Default-Implementierungen der Klassenfunktionen Eq a where (==), (/=) Pseudo-Syntax für den Kopf: x /= y x == y class [OBERKLASSE =>] Klassenname a where ... Typdeklarationen und Default-Implementierungen ... :: a -> a -> Bool = not (x == y) = not (x /= y) definiert die Klasse Klassenname Keine Klassenbedingung a ist der Parameter für den Typ. Es ist nur eine solche Variable erlaubt Klassenmethoden sind == und /= OBERKLASSE ist eine Klassenbedingung (optional) Instanzen müssen mindestens == oder /= definieren Es gibt Default-Implementierungen für beide Klassenmethoden Einrückung beachten! EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 17/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Typklasseninstanz (1) 18/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Typklasseninstanz (2) Beispiele: Instanzen definieren die Klassenmethoden für einen konkreten Typ Instanzen können Default-Implementierungen überschreiben instance Eq Bool where x == y = (x && y) || (not x && not y) x /= y = not (x == y) Syntax für Instanzen: instance [KLASSENBEDINGUNGEN => ] KLASSENINSTANZ where ...Implementierung der Klassenmethoden ... instance Eq Montag Dienstag Mittwoch Donnerstag Freitag Samstag Sonntag _ KLASSENINSTANZ besteht aus Klasse und der Instanz, z.B. Eq [a] oder Eq Int KLASSENBEDINGUNGEN optional EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel instance Eq Int where (==) = primEQInt 19/59 Wochentag where == Montag = == Dienstag = == Mittwoch = == Donnerstag = == Freitag = == Samstag = == Sonntag = == _ = EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel True True True True True True True False 20/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Typklasseninstanz (3) Eq == == == Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Fehlende Definitionen class Beispiel mit Klassenbedingung: instance [] (x:xs) _ Module Typklassen a => Eq [a] where [] = True (y:ys) = (x == y) && (xs == ys) _ = False Eq a where (==), (/=) x /= y x == y :: a -> a -> Bool = not (x == y) = not (x /= y) Beispiel: data RGB = Rot | Gruen | Blau deriving(Show) Für die Abfrage x == y muss der Gleichheitstest auf Listenelementen vorhanden sein instance Eq RGB where Eq a => Eq [a] drückt diese Bedingung gerade aus. Typ [a] ist nur dann eine Instanz von Eq, ” wenn Typ a bereits Instanz von Eq ist“ Instanz syntaktisch korrekt Keine Fehlermeldung oder Warnung Rot == Gruen terminiert nicht! EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 21/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Fehlende Definitionen (2) 22/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Typklassen: Vererbung Eigene Eq“-Klasse ” class (Oberklasse1 a, ..., OberklasseN a) => Klassenname a where ... class MyEq a where (===), (=/=) :: a -> a -> Bool (===) a b = not (a =/= b) Oberklasse1 a Nur (===) hat eine Default-Implementierung Oberklasse2 a ... OberklasseN a instance MyEq RGB where Instanz syntaktisch korrekt Warnung vom Compiler (kein Fehler): Klassenname a Warning: No explicit method nor default method for ‘=/=’ In the instance declaration for ‘MyEq RGB’ Klassenname ist Unterklasse von Oberklasse1,...,OberklasseN Rot == Gruen gibt Laufzeitfehler: In der Klassendefinition können die überladenen Operatoren der Unterklasse verwendet werden (da sie geerbt sind) *Main> Rot === Gruen *** Exception: TypklassenBeispiele.hs:37:9-16: No instance nor default method for class operation Main.=/= EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Beachte: => ist nicht als logische Implikation zu interpretieren Da mehrere Oberklassen erlaubt sind, ist Mehrfachvererbung möglich 23/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 24/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Klasse mit Vererbung: Ord Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Beispiel: Instanz für Wochentag Ord ist Unterklasse von Eq: class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a compare x y | x == y = EQ | x <= y = LT | otherwise = GT x x x x <= < >= > y y y y = = = = max x y | | min x y | | compare compare compare compare x x x x y y y y /= == /= == x <= y otherwise x <= y otherwise = = = = y x x y Ordering ist definiert als: data Ordering = LT | EQ | GT GT LT LT GT Instanzen müssen entweder <= oder compare definieren. EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 25/59 Module Typklassen instance Ord Wochentag where a <= b = (a,b) ‘elem‘ [(a,b) | i <- [0..6], let a = ys!!i, b <- drop i ys] where ys = [Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag] EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Vererbung im Typsystem Module Typklassen 26/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Klassenbeschränkungen bei Instanzen instance (Klasse1 a1, ..., KlasseN aN) => Klasse Typ where ... Beispiel: f x y = (x == y) && (x <= y) Keine Vererbung! Da == und <= verwendet werden würde man erwarten: Meint: Es gibt nur dann eine Instanz, wenn es Instanzen für die Typvariablen a1,. . . ,aN der entsprechenden Klassen gibt. Typ von f? Die Typvariablen a1,. . . ,aN müssen alle im Typ Typ vorkommen. f :: (Eq a, Ord a) => a -> a -> Bool Beispiel: Da Ord jedoch Unterklasse von Eq: instance Eq a => Eq (BBaum a) where Blatt a == Blatt b = a == b Knoten l1 r1 == Knoten l2 r2 = l1 == l2 && r1 == r2 f :: Ord a => a -> a -> Bool Nur wenn man Blattmarkierungen vergleichen kann, dann auch Bäume EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 27/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 28/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Klassenbeschränkungen bei Instanzen (2) Klassenbeschränkungen bei Instanzen (3) *Main List> Blatt 1 == Blatt 2 False *Main List> Blatt 1 == Blatt 1 True *Main List> Blatt (\x ->x) == Blatt (\y -> y) Beispiel mit mehreren Klassenbeschränkungen: data Either a b = Left a | Right b Either-Instanz für Eq: <interactive>:1:0: No instance for (Eq (t -> t)) arising from a use of ‘==’ at <interactive>:1:0-32 Possible fix: add an instance declaration for (Eq (t -> t)) In the expression: Blatt (\ x -> x) == Blatt (\ y -> y) In the definition of ‘it’: it = Blatt (\ x -> x) == Blatt (\ y -> y) EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen instance Left x Right x _ 29/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Die Klassen Read und Show (Eq a, Eq b) == Left y = == Right y = == _ = => Either a b where x == y -- benutzt Eq-Instanz für a x == y -- benutzt Eq-Instanz für b False EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 30/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Die Klassen Read und Show (2) Die Klassen Read und Show dienen zum Einlesen (Parsen) und Anzeigen von Datentypen. class Read a where readsPrec :: Int -> ReadS a readList :: ReadS [a] -- ... default decl for readList given in Prelude show :: Show a => a -> String read :: Read a => String -> a Allerdings ist read keine Klassenfunktion! Komplizierter: type type class Show a where showsPrec :: Int -> a -> ShowS show :: a -> String showList :: [a] -> ShowS ReadS a = String -> [(a,String)] ShowS = String -> String ReadS ist wie ein Parser mit Erfolgslisten ShowS kann noch einen String als Argument nehmen (dadurch oft schneller als ++) showsPrec _ x s = show x ++ s show x = showsPrec 0 x "" -- ... default decl for showList given in Prelude reads :: Read a => ReadS a shows :: Show a => a -> ShowS EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 31/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 32/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Show für BBaum showBBaum showBBaum showBBaum "<" ++ Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Show für BBaum (2) :: (Show t) => BBaum t -> String (Blatt a) = show a (Knoten l r) = showBBaum l ++ "|" ++ showBBaum r ++ ">" Tests: *Main> last $ showBBaum t ’>’ (73.38 secs, 23937939420 bytes) *Main> last $ showBBaum’ t ’>’ (0.16 secs, 10514996 bytes) Schlecht, da u.U. quadratische Laufzeit. Besser: Hierbei ist t ein Baum mit ca. 15000 Knoten. showBBaum’ :: (Show t) => BBaum t -> String showBBaum’ b = showsBBaum b [] Show-Instanz für BBaum a: showsBBaum :: (Show t) => BBaum t -> ShowS showsBBaum (Blatt a) = shows a showsBBaum (Knoten l r) = showChar ’<’ . showsBBaum l . showChar ’|’ . showsBBaum r . showChar ’>’ EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen instance Show a => Show (BBaum a) where showsPrec _ = showsBBaum 33/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Read für BBaum Module Typklassen 34/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung erfordert manchmal Typ Prelude> read "10" Elegant mit List Comprehensions: <interactive>:1:0: Ambiguous type variable ‘a’ in the constraint ‘Read a’ arising from a use of ‘read’ at <interactive>:1:0-8 Probable fix: add a type signature that fixes these type variable(s) instance Read a => Read (BBaum a) where readsPrec _ = readsBBaum readsBBaum :: (Read a) => ReadS (BBaum a) readsBBaum (’<’:xs) = [(Knoten l r, rest) | (l, ’|’:ys) <- readsBBaum xs, (r, ’>’:rest) <- readsBBaum ys] readsBBaum s = [(Blatt x, rest) | (x,rest) <- reads s] Prelude> (read "10")::Int 10 Ähnliches Problem bei überladenen Konstanten, z.B. 0 Im GHC Defaulting: Für Zahlen ist dies der Typ Integer. EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 35/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 36/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Klassen mit Mehrfachvererbung: Num Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Die Klasse Enum Num überlädt Operatoren für Zahlen: class (Eq a, Show (+), (-), (*) negate abs, signum fromInteger a) :: :: :: :: Enum ist für Typen geeignet deren Werte abzählbar sind: => Num a where a -> a -> a a -> a a -> a Integer -> a class Enum a where succ, pred :: toEnum :: fromEnum :: enumFrom :: enumFromThen :: enumFromTo :: enumFromThenTo :: Mit fromInteger werden Zahlenkonstanten überladen, z.B. length [] = 0 length (x:xs) = 1+(length xs) a -> a Int -> a a -> Int a -> [a] a -> a -> [a] a -> a -> [a] a -> a -> a -> [a] ----- [n..] [n,n’..] [n..m] [n,n’..m] Der Compiler kann den Typ length :: (Num a) => [b] -> a herleiten, da 0 eigentlich für fromInteger (0::Integer) steht. EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 37/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Die Klasse Enum (2) EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 38/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Typisierung unter Typklassen Syntax von polymorphen Typen ohne Typklassen Enum-Instanz für Wochentag T ::= T V | T C T1 . . . Tn | T1 → T2 instance Enum Wochentag where toEnum i = tage!!(i ‘mod‘ 7) fromEnum t = case elemIndex t tage of Just i -> i Erweiterte Typen Te mit Typklassen: Te ::= T | Kon => T tage = [Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag] Ein Aufruf: Kon ist ein Typklassenkontext: Kon ::= Klassenname T V | (Klassenname1 T V, . . . , Klassennamen T V ) *Main> enumFromTo Montag Sonntag [Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag] Zusätzlich: Für Kontext => Typ muss gelten: Alle Typvariablen des Kontexts kommen auch im Typ Typ vor. Z.B. elem ::(Eq a) => a -> [a] -> Bool. EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 39/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 40/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Konstruktorklassen Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Die Konstruktorklasse Functor Die bisherigen Klassen abstrahieren über einen Typ Z.B. Definition der Klasse Functor: class Functor f where fmap :: (a -> b) -> f a -> f b class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y) x == y = not (x /= y) f ist eine Variable für einen Typkonstruktor. für die Variable a kann ein Typ eingesetzt werden. Functor-Instanz für BBaum In Haskell ist auch möglich über Typkonstruktoren zu abstrahieren instance Functor BBaum where fmap = bMap Solche Klassen heißen Konstruktorklassen Bei Instanzbildung muss der Typkonstruktor BBaum und nicht der Typ (BBaum a) angegeben werden. Zu Erinnerung: Z.B. ist BBaum a ein Typ aber BBaum ein Typkonstruktor. EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 41/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Die Konstruktorklasse Functor Module Typklassen 42/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Kinds Falsch mit Baum a statt Baum: Kinds sind quasi Typen über Typen instance Functor (BBaum a) where fmap = bMap Syntax: K ::= ∗ | K -> K Hierbei steht ∗ für einen Typen ergibt den Fehler: Beispiele: Kind mis-match The first argument of ‘Functor’ should have kind ‘* -> *’, but ‘BBaum a’ has kind ‘*’ In the instance declaration for ‘Functor (BBaum a)’ EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 43/59 • der Typkonstruktor BBaum hat den Kind * -> *, • der Typ Int hat den Kind *, • der Typkonstruktor Either hat den Kind * -> * -> *. EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 44/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Instanzen von Functor Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Übersicht über die vordefinierten Typklassen instance Functor (Either a) where fmap f (Left a) = Left a fmap f (Right a) = Right (f a) instance Functor Maybe where fmap f Nothing = Nothing fmap f (Just a) = Just (f a) Instanzen von Functor sollten die folgenden beiden Gesetze erfüllen: Eq Show Read alle außer IO, -> alle außer IO, -> alle außer IO, -> Bounded Ord Num alle außer IO, IOError, -> Int, Integer, Float, Double Int, Char, Bool, (), Ordering, Tupel Enum Real Fractional (), Bool, Char, Ordering, Int, Integer, Float, Double Int, Integer, Float, Double Float, Double Integral RealFrac Floating Int, Integer Float, Double Float, Double RealFloat fmap id = id fmap (f . g) Float, Double = fmap f . fmap g EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 45/59 Monad IO, [], Maybe IO, [], Maybe EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung Functor Module Typklassen 46/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (2) Irgendwann muss die richtige Implementierung für eine überladene Funktion benutzt werden Wir machen das anhand von Beispielen: Auflösung von Eq Early Binding: Auflösung zur Compilezeit class Late Binding: Auflösung zur Laufzeit In Haskell Es gibt keine Typinformation zur Laufzeit Anstelle der Typklasse tritt eine Datentypdefinition: =⇒ Auflösung zur Compilezeit Dieser Dictionary-Datentyp ist ein Produkttyp, der für jede Klassenmethode eine Komponente erhält. Auflösung benötigt Typinformation, daher Auflösung zusammen mit dem Typcheck Unser Vorgehen: data EqDict a = EqDict { eqEq :: a -> a -> Bool, -- f"ur == eqNeq :: a -> a -> Bool -- f"ur /= } Wir nehmen an Typinformation ist vorhanden Wir trennen jedoch Auflösung und Typcheck, d.h. wir behandeln erstmal nur die Auflösung EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y) x == y = not (x /= y) 47/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 48/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (3) Module Typklassen Auflösung der Überladung (4) Default-Implementierungen: Sie werden als normale“ Funktionen implementiert ” und erhalten als zusätzliches Argument das Dictionary Beachte, dass bei der Übersetzung aus (==) -- Default-Implementierung f"ur ==: default_eqEq eqDict x y = not (eqNeq eqDict x y) overloadedeq :: EqDict a -> a -> a-> Bool Überladene Funktionen können nun durch zusätzliche Dictionary-Parameter angepasst werden: Beispiel: Anstelle der überladenen Operatoren: -- Ersatz f"ur == overloadedeq :: EqDict a -> a -> a -> Bool overloadedeq dict a b = eqEq dict a b elemEq :: EqDict a -> elem :: (Eq a) => a -> [a] -> Bool elemEq dict e [] = elem e [] = False elem e (x:xs) =⇒ elemEq dict e (x:xs) | (eqEq dict) e x | e == x = True | otherwise | otherwise = elem e xs -- Ersatz f"ur /= overloadedneq :: EqDict a -> a -> a -> Bool overloadedneq dict a b = eqNeq dict a b EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel :: Eq a => a -> a -> Bool wird: -- Default-Implementierung f"ur /=: default_eqNeq eqDict x y = not (eqEq eqDict x y) Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen 49/59 = True = elemEq dict e xs EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (5) a -> [a] -> Bool False Module Typklassen 50/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (6) Woher erhält man die konkreten Dictionaries? Aus der Instanzdefinition: Bei konkreten Typen müssen jedoch die konkreten Dictionaries eingesetzt werden instance Eq Montag Dienstag Mittwoch Donnerstag Freitag Samstag Sonntag _ Z.B. aus ... True == False ... wird ... overloadedeq eqDictBool True False Wochentag where == Montag = == Dienstag = == Mittwoch = == Donnerstag = == Freitag = == Samstag = == Sonntag = == _ = eqDictWochentag :: EqDict Wochentag True eqDictWochentag = True EqDict { True eqEq = eqW, True eqNeq = default_eqNeq eqDictWochentag True } True where eqW Montag Montag = True True eqW Dienstag Dienstag = True ⇒ False eqW Mittwoch Mittwoch = True eqW Donnerstag Donnerstag = True eqW Freitag Freitag = True eqW Samstag Samstag = True eqW Sonntag Sonntag = True eqW _ _ = False Beachte die Verwendung der Default-Implementierung für eqNeq EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 51/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 52/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (7) Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (8) Instanzen mit Klassenbeschränkungen: instance Eq a => Eq (BBaum a) where Blatt a == Blatt b = a == b Knoten l1 r1 == Knoten l2 r2 = l1 == l2 && r1 == r2 Eq-Dictionary für Ordering instance Eq Ordering where LT == LT = True EQ == EQ = True GT == GT = True _ == _ = False ⇒ eqDictOrdering :: EqDict Ordering eqDictOrdering = EqDict { eqEq = eqOrdering, eqNeq = default_eqNeq eqDictOrdering } where eqOrdering LT LT = True eqOrdering EQ EQ = True eqOrdering GT GT = True eqOrdering _ _ = False EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Auflösung: das Dictionary für BBaum a ist eine Funktion, Diese erhält das Dictionary für a als Argument: eqDictBBaum :: EqDict a -> EqDict (BBaum a) eqDictBBaum dict = EqDict { eqEq = eqBBaum dict, eqNeq = default_eqNeq (eqDictBBaum dict) } where eqBBaum dict (Blatt a) (Blatt b) = overloadedeq dict a b -- hier Eq-Dictionary f"ur dict eqBBaum dict (Knoten l1 r1) (Knoten l2 r2) = eqBBaum dict l1 l2 && eqBBaum dict r1 r2 53/59 Module Typklassen EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (9) 54/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (10) Auflösung bei Unterklassen: Übersetzung der Default-Implementierungen: Der Datentyp enthält die Dictionaries der Oberklassen class (Eq a) => compare :: a (<), (<=), (>=), (>) :: a max, min :: a Ord a where -> a -> Ordering compare x y | x == y = EQ | x <= y = LT | otherwise = GT -> a -> Bool -> a -> a data OrdDict a = OrdDict { eqDict :: EqDict a, compare x y | x == y = EQ | x <= y = LT | otherwise = GT =⇒ x x x x <= < >= > y y y y = = = = max x y | | min x y | | compare compare compare compare x x x x y y y y /= == /= == x <= y otherwise x <= y otherwise = = = = y x x y GT LT LT GT EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel -- Dictionary -- der Oberklasse ordCompare :: a -> a -> Ordering, ordL :: a -> a -> Bool, ordLT :: a -> a -> Bool, ordGT :: a -> a -> Bool, ordG :: a -> a -> Bool, ordMax :: a -> a -> a, ordMin :: a -> a -> a } x <= y x < y ⇒ default_ordCompare dictOrd x y | (eqEq (eqDict dictOrd)) x y = EQ | (ordLT dictOrd) x y = LT | otherwise = GT ⇒ default_ordLT let compare nequal in (compare ⇒ default_ordL dictOrd x y = let compare = (ordCompare dictOrd) equal = eqEq eqDictOrdering in (compare x y) ‘equal‘ LT = compare x y /= GT = compare x y == LT dictOrd x y = = (ordCompare dictOrd) = eqNeq (eqDictOrdering) x y) ‘nequal‘ GT usw. 55/59 EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 56/59 Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (11) Module Typklassen Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Auflösung der Überladung (11) Ord-Dictionary für Wochentag: Konstruktorklassen: instance Ord Wochentag where a <= b = (a,b) ‘elem‘ [(a,b) | i <- [0..6], let a = ys!!i, b <- drop i ys] where ys = [Montag,Dienstag,Mittwoch,Donnerstag,Freitag,Samstag,Sonntag] class Functor f where fmap :: (a -> b) -> f a -> f b ⇒ Typvariablen a und b müssen für dem Dictionary hinzugefügt werden: ordDictWochentag = OrdDict { eqDict = eqDictWochentag, ordCompare = default_ordCompare ordDictWochentag, ordL = default_ordL ordDictWochentag, ordLT = wt_lt, ordGT = default_ordGT ordDictWochentag, ordG = default_ordG ordDictWochentag, ordMax = default_ordMax ordDictWochentag, ordMin = default_ordMin ordDictWochentag } where wt_lt a b = (a,b) ‘elem‘ [(a,b) | i <- [0..6], let a = ys!!i, b <- drop i ys] ys = [Montag, Dienstag, Mittwoch, Donnerstag, Freitag, Samstag, Sonntag] EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel Module Typklassen 57/59 Klassen+Instanzen Konstruktorkl. Auflösung Erweiterungen Erweiterung von Typklassen Multiparameter Klassen Haskell erlaubt nur eine Kindvariable in der Klassendeklaration Multiparameter-Klassen erlaubt auch mehrere Kindvariablen, z.B. class Indexed c a i where sub :: c -> i -> a idx :: c -> a -> Maybe i Überlappende bzw. flexible Instanzen sind in Haskell verboten: instance Eq (Bool,Bool) where (a,b) == (c,d) = ... Funktionale Abhängigkeiten class MyClass a b | a -> b where meint in etwa: Der Typ b wird durch den Typ a bestimmt. Problem aller Erweiterungen: Typsystem wird unentscheidbar! EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 59/59 data FunctorDict a b f = FunctorDict { functorFmap :: (a -> b) -> f a -> f b} Die überladene fmap-Funktion nach der Übersetzung: overloaded_fmap :: (FunctorDict a b f) -> (a -> b) -> f a -> F b overloaded_fmap dict = functorFmap dict Instanz für BBaum: functorDictBBaum = FunctorDict {functorFmap = bMap} EFP – (06) Haskell (2) – WS 2010/11 – D. Sabel 58/59