Einführung in die Funktionale Programmierung: [1.5ex] Haskell (2

Werbung
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
Herunterladen