Type Constraints in Intanziierungen

Werbung
10 · Typklassen und Overloading
Typklassen · 10.3
Type Constraints in Intanziierungen
�
Gleichheit von Listen des Typs [α] ist offensichtlich nur dann
wohldefiniert, wenn auch α Gleichheitstests zulässt.
�
Dies kann durch einen Type Constraint auf dem Klassenparameter α
ausgedrückt werden:
1
�
instance Eq α => Eq [α] where
2
3
4
5
[]
== []
= True
(x:xs) == (y:ys) = x == y
_
== _
= False
&&
xs == ys
6
7
�
xs /= ys
= not (xs == ys)
Die (==)-Operatoren
entstammen verschiedenen
Instanziierungen.
(was ist damit gemeint?)
Damit sind jetzt automatisch alle Listen über vergleichbaren Typen
vergleichbar (des rechtfertigt die Syntax =>).
Frage Wie könnten Paare des Typs (α,β) für den Gleichheitstest
zugelassen werden?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
314
10 · Typklassen und Overloading
Typklassen · 10.3
Antwort Die Instanziierung von Eq (α, β) kommt aus der Prelude, und
könnte etwa so aussehen:
1
instance (Eq α, Eq β) => Eq (α, β) where
2
(x, y) == (v, w) = x == v && y == w
3
4
p
5
�
/= q
= not (p == q)
Vergleiche die letzte Zeile mit der Instanziierung von Eq [α].
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
315
10 · Typklassen und Overloading
Typklassen · 10.3
class Defaults
�
�
Oft sind default-Definitionen für die Funktionen einer Typklasse sinnvoll
(s. Eq und (/=)).
Eine default-Definition wird innerhalb des class-Konstruktes
vorgenommen. Jede Instanz dieser Typklasse “erbt” diese Definition,
wenn sie nicht explizit überschrieben wird.
Beispiel Allgemein besteht
eine offensichtliche Beziehung
zwischen Gleichheit und
Ungleichheit.
1
2
3
4
5
6
7
�
�
class Eq a where
— Minimal complete definition: (==) | (/=)
(==) :: a -> a -> Bool
x == y = not (x /= y)
(/=) :: a -> a -> Bool
x /= y = not (x == y)
Bei der Instanziierung muss jetzt nur noch (mindestens!) eine der
Definitionen überschrieben werden.
Defaults erlauben es konsistentes Verhalten der Funktionen einer Klasse
nahezulegen (nicht: erzwingen).
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
316
10 · Typklassen und Overloading
Typklassen · 10.3
Beispiel Typen mit Ordnung können die Funktionen der Klasse Ord
(data Ordering = EQ | LT | GT)
überladen:
1
2
3
4
class Ord α where
compare
:: α -> α -> Ordering
(<), (<=), (>=), (>) :: α -> α -> Bool
max, min
:: α -> α -> α
5
6
7
8
9
— Minimal complete definition: (<=) | compare
compare x y | x == y
= EQ
| x <= y
= LT
| otherwise = GT
10
11
12
13
14
x
x
x
x
<=
<
>=
>
y
y
y
y
=
=
=
=
compare
compare
compare
compare
x
x
x
x
y
y
y
y
/=
==
/=
==
GT
LT
LT
GT
=
=
=
=
x
y
x
y
15
16
17
18
19
max x y |
|
min x y |
|
x >= y
otherwise
x <= y
otherwise
Frage Steht hier die “ganze Wahrheit”? Oder haben wir etwas vergessen?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
317
10 · Typklassen und Overloading
Typklassen · 10.3
Antwort compare verwendet ==, benötigt also einen Eq α Kontext:
1
2
3
4
class Eq α => Ord α where
compare
:: α -> α -> Ordering
(<), (<=), (>=), (>) :: α -> α -> Bool
max, min
:: α -> α -> α
5
— Minimal complete definition: (<=) or compare
compare x y | x == y
= EQ
| x <= y
= LT
| otherwise = GT
6
7
8
9
— Hierfür brauchen wir Eq α, ...
10
x
x
x
x
11
12
13
14
<=
<
>=
>
y
y
y
y
=
=
=
=
compare
compare
compare
compare
x
x
x
x
y
y
y
y
/=
==
/=
==
GT
LT
LT
GT
=
=
=
=
x
y
x
y
— ... hierfür nicht. Warum?
15
max x y |
|
min x y |
|
16
17
18
19
�
�
x >= y
otherwise
x <= y
otherwise
Auch Klassendefinitionen können einen Type Constraint aufweisen
Man darf einen Typ nur dann zu Ord instanziieren, wenn er auch
vergleichbar ist!
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
318
10 · Typklassen und Overloading
Typklassen · 10.3
Zusammenfassung der Syntax
38
Ein Type Constraint kann mehrere Klassen oder Typvariablen beinhalten:
(==)
:: Eq α ⇒ α → α → Bool
λx. x + 1 > 3
:: (Num α, Ord α) ⇒ α → Bool
λx y z. (x + 1, y > z) :: (Num α, Ord β) ⇒ α → β → β → (α, Bool)
�
Formal hat ein Type Constraint also die Form
� TypeConstraint
OneConstraint
�
→
|
→
OneConstraint =>
( OneConstraint (, OneConstraint)∗ ) =>
ClassName TypeVariable
Klassendefinition und Instanziierung können Type Constraints
enthalten:
class TypeConstraint? C α where
Deklarationen und Default-Definitionen
instance TypeConstraint? C α where
Definitionen
38 Genauer:
https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-750004.3
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
319
10 · Typklassen und Overloading
10.4
Instanziierung algebraischer Datentypen
Beispiel
1
�
2
3
4
5
6
7
8
9
10
Erinnerung: Typ Weekday.
data Weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun
�
1
Instanziierung algebraischer Datentypen · 10.4
Wie macht man Weekday zu einem aufzählbaren Datentyp (also zu einer
Instanz von Enum)?
Die Prelude definiert39 Defaults für alle Funktionen in Enum bis auf
toEnum und fromEnum:
class Enum a where
toEnum
:: Int -> a
fromEnum
:: a -> Int
.
.
.
succ :: a -> a
succ = toEnum . (+1) . fromEnum
.
.
.
enumFromTo :: a -> a -> [a]
enumFromTo x y = map toEnum [ fromEnum x .. fromEnum y ]
.
.
.
39 http://hackage.haskell.org/package/base-4.8.0.0/docs/Prelude.html#t:Enum
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
320
10 · Typklassen und Overloading
Instanziierung algebraischer Datentypen · 10.4
Verbleibt also die Definition von
toEnum und fromEnum mit der
offensichtlich gewünschten
Eigenschaft fromEnum ◦ toEnum ≡ id.
Hinweis fromEnum und toEnum
etablieren einen Isomorphismus von
Weekday und [0..6].
1
2
3
4
5
6
7
8
9
10
11
12
Jetzt geht zum Beispiel:
1
2
13
*Main> [Wed .. Sat] — Leerzeichen wichtig!
[Wed,Thu,Fri,Sat]
14
15
instance Enum Weekday where
toEnum 0 = Mon
toEnum 1 = Tue
toEnum 2 = Wed
toEnum 3 = Thu
toEnum 4 = Fri
toEnum 5 = Sat
toEnum 6 = Sun
fromEnum Mon = 0
fromEnum Tue = 1
fromEnum Wed = 2
fromEnum Thu = 3
fromEnum Fri = 4
fromEnum Sat = 5
fromEnum Sun = 6
Problem Die Definition derartiger Datentypen als Instanz von Enum (aber
auch Eq, Ord, Show, Read40 ) ist kanonisch, aufwändig, und fehleranfällig.
40 Einige
dieser Typklassen tauchen später noch auf.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
321
10 · Typklassen und Overloading
Instanziierung algebraischer Datentypen · 10.4
Beispiel Wir machen BinTree zur Instanz von Eq, und definieren dazu
den Gleichheitstest auf Werten von BinTree:
1
2
3
4
instance Eq a => Eq (BinTree a) where
Empty
== Empty
= True
Node l1 x1 r1 == Node l2 x2 r2 = x1 == x2 && l1 == l2 && r1 == r2
_
== _
= False
Beobachtungen (reguläres Muster):
�
Die Struktur der Definition von (==) folgt der rekursiven Struktur des
Datentyps BinTree.
�
Nur jeweils durch gleiche Konstruktoren erzeugte Werte können
potentiell gleich sein.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
322
10 · Typklassen und Overloading
Instanziierung algebraischer Datentypen · 10.4
deriving
�
Das soeben beobachtete reguläre Muster der Typklassen-Operationen
ermöglicht es dem Haskell-Compiler, instance-Deklarationen
automatisch abzuleiten.
Syntax
Die deriving-Klausel...
data T α1 α2 ... αn = ...
| ...
..
.
deriving (C1 ,...,Cm )
...macht den Typkonstruktor T zur Instanz der Typklassen C1 , ...Cm .
�
Diese Magie ist leider nicht für alle Typklassen Ci verfügbar.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
323
10 · Typklassen und Overloading
Instanziierung algebraischer Datentypen · 10.4
Die Definitionen der Typklassen-Operationen können automatisch erzeugt
werden, für folgende in der Prelude definierten Klassen:
Eq Ableitbar für alle Datentypen mit vergleichbaren Komponenten.
� Gleichheit wird über die Identität von Konstruktoren und
(rekursiv) der Gleichheit der Komponenten des Datentyps
entschieden.
Ord Ableitbar für alle Datentypen mit anordenbaren Komponenten.
� Reihenfolge der Konstruktoren in data-Deklaration
entscheidend, Ordnung wird lexikographisch (rekursiv) definiert.
Enum Datentyp muss ein reiner Summentyp sein (s. vorn), also
data T = K0 | ... | Kn−1
mit fromEnum Ki = i.
Show Textuelle Repräsentation der Konstruktoren, s. später.
... Einige andere die wir hier nicht besprechen.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
324
10 · Typklassen und Overloading
Beispiel
1
2
3
Instanziierung algebraischer Datentypen · 10.4
BinTree (nochmal)
data BinTree a = Empty
| Node (BinTree a) a (BinTree a)
deriving (Eq, Ord)
führt automatisch zu:
1
2
3
4
instance Eq a => Eq (BinTree a) where
Empty
== Empty
= True
Node l1 x1 r1 == Node l2 x2 r2 = x1 == x2 && l1 == l2 && r1 == r2
_
== _
= False
5
6
7
8
9
10
11
12
instance Ord a => Ord (BinTree a) where
Empty
<= Empty
= True
Empty
<= Node _ _ _ = True
Node _ _ _ <= Empty
= False
Node l1 x1 r1 <= Node l2 x2 r2 = l1 < l2
|| l1 == l2 && x1 < x2
|| x1 == x2 && r1 <= r2
Hinweis zu den == Operatoren: Der Type Constraint Ord α impliziert
bereits Eq α. Das kommt aus der Klassendefinition von Ord, cf. Seite 318
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
325
10 · Typklassen und Overloading
Instanziierung algebraischer Datentypen · 10.4
Beispiel Nicht immer ist deriving die Antwort!
Erinnerung – Frac repräsentiert rationale Zahlen.
1
2
data Frac = Integer :/ Integer
deriving Eq
Hier führt deriving nicht zum Ziel ...
1
2
> 2 :/ 5 == 4 :/ 10
False
Gemeint ist vielmehr:
1
2
instance Eq Frac where
(x1 :/ y1) == (x2 :/ y2)
=
x1 * y2 == x2 * y1
3
4
5
> 2 :/ 5 == 4 :/ 10
True
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
326
10 · Typklassen und Overloading
Beispiel
1
2
Instanziierung algebraischer Datentypen · 10.4
Weekday (nochmal)
data Weekday = Mon | Tue | Wed | Thu | Fri | Sat | Sun
deriving (Eq, Ord, Enum, Show)
Damit haben wir automagisch:
1
2
3
4
5
6
7
8
> Mon < Tue
True
> Mon == Sat
False
> [Mon,Wed .. Sun]
[Mon,Wed,Fri,Sun]
> fromEnum Sat
5
�
Frage Was ergibt der Aufruf map toEnum [2,3]?
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
327
10 · Typklassen und Overloading
Anmerkung: Mehrdeutige Typen
10.5
�
Anmerkung: Mehrdeutige Typen · 10.5
Die Funktion show :: Show α ⇒ α → String wird vom GHCi
verwendet, um das Ergebnis einer Berechnung auszugeben (cf. Seite 334).
1
2
3
4
5
6
*Main> ['1', '2', '3']
"123"
*Main> [1,2,3]
[1,2,3]
*Main> 123
123
1
2
3
4
5
6
*Main> show ['1', '2', '3']
"\"123\""
*Main> show [1,2,3]
"[1,2,3]"
*Main> show 123
"123"
�
Offensichtlich muss show für verschiedene Typen unterschiedlich
implementiert sein.
⇒ Je nach Typ wird die passende Implementation von show ausgewählt.
�
Welche Implementation soll für toEnum 2 ausgewählt werden?
1
2
*Main> :t toEnum 2
toEnum 2 :: Enum a => a
Stefan Klinger · DBIS
Vollkommen unklar! α kann mit jedem
beliebigen Typen aus der Klasse Enum
instanziiert werden.
Informatik 2 · Sommer 2016
328
10 · Typklassen und Overloading
Anmerkung: Mehrdeutige Typen · 10.5
Fehlermeldung
1
2
3
4
5
6
7
8
9
10
bis GHCi 7.6.3, sonst cf. Seite 332
Prelude> toEnum 2
<interactive>:2:1:
No instance for (Enum a0) arising from a use of ‘toEnum’
The type variable ‘a0’ is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there are several potential instances:
instance Enum Weekday -- Defined at <interactive>:3:35
instance Enum Double -- Defined in ‘GHC.Float’
instance Enum Float -- Defined in ‘GHC.Float’
...plus 8 others
�
�
Der GHCi sagt uns dass er nicht weiss
welches Enum wir meinen.
Wir können aber für einen Ausdruck
explizit einen Typ angeben:
1
2
3
4
5
6
7
1
2
3
4
*Main> :t 4
4 :: Num a => a
*Main> 4
4
Stefan Klinger · DBIS
8
*Main>
GT
*Main>
'\STX'
*Main>
Wed
*Main>
2
toEnum 2 :: Ordering
toEnum 2 :: Char
— cf. ascii(7)
toEnum 2 :: Weekday
toEnum 2 :: Int
Frage Was passiert hier? Warum kein Fehler?
Informatik 2 · Sommer 2016
329
10 · Typklassen und Overloading
Anmerkung: Mehrdeutige Typen · 10.5
Type Defaulting
Antwort Offensichtlich sucht sich der GHCi einen “passenden Typ” aus.
�
Auf Dauer wäre es anstrengend, auch bei einfachen Fällen (z.B. Zahlen)
immer einen Typ mit angeben zu müssen.
1
�
�
Dieses Vorgehen heisst Type
Defaulting und wird auch für
andere Fälle verwendet:
2
3
4
5
6
*Main> []
— [ ] :: ∀α. [α]
[]
*Main> [] :: String
""
*Main> 2.3
— 2.3 :: Fractional α ⇒ α
2.3
Die Defaulting-Regeln sind im Haskell-Standard41 und im GHCi
Manual42 beschrieben.
41 https://www.haskell.org/onlinereport/haskell2010/haskellch4.html#x10-790004.3.4
42 https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/interactive-evaluation.html#extended-default-rules
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
330
10 · Typklassen und Overloading
�
1
2
3
4
5
6
7
Anmerkung: Mehrdeutige Typen · 10.5
Man kann den ghci mit der Option -Wall starten43 , dann wird ein
Defaulting-Vorgang angezeigt.
Prelude> 5
<interactive>:3:1: Warning:
Defaulting the following constraint(s) to type ‘Integer’
(Show a0) arising from a use of ‘print’ at <interactive>:3:1
(Num a0) arising from a use of ‘it’ at <interactive>:3:1
In a stmt of an interactive GHCi command: print it
5
8
9
10
11
12
13
14
Prelude> []
<interactive>:4:1: Warning:
Defaulting the following constraint(s) to type ‘()’
(Show t0) arising from a use of ‘print’
In a stmt of an interactive GHCi command: print it
[]
Der Unit-Type () enthält genau einen Wert, () :: (), ebenfalls Unit
genannt. Er wird oft verwendet wenn man einen Typ braucht der weiter
kaum Eigenschaften hat.
43 -W
legt fest welche Warnungen gezeigt werden sollen. Hier: all = Alle.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
331
10 · Typklassen und Overloading
Anmerkung: Mehrdeutige Typen · 10.5
Erweiterte Defaulting-Regeln
GHCi ab Version 7.8
Die Defaulting-Regeln wurden im Laufe der Zeit erweitert44 .
�
Aktuelle Versionen des GHCi verwenden oft () als Default.
1
2
3
GHCi, version 7.8.4: http://www.haskell.org/ghc/
Prelude> :set -Wall
Prelude> toEnum 3
:? for help
4
5
6
7
8
�
�
<interactive>:3:1: Warning:
Defaulting the following constraint(s) to type ‘()’
...
*** Exception: Prelude.Enum.().toEnum: bad argument
Hier wird also toEnum 3 :: Enum α ⇒ zu toEnum 3 :: () konkretisiert.
Die Fehlermeldung wird dann von der konkreten Funktion toEnum 3 :: ()
ausgegeben:
• () ist zwar Instanz der Enum-Klasse,
• enthält aber nur einen Wert! (Ausprobieren: toEnum 0)
44 https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/interactive-evaluation.html#extended-default-rules
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
332
10 · Typklassen und Overloading
�
Anmerkung: Mehrdeutige Typen · 10.5
Obacht
�
Defaulting ist eine Krücke die die Arbeit mit dem interaktiven
Interpreter vereinfachen soll.
�
Dadurch wird die Sprache Haskell nicht klarer, oder einfacher,
vielmehr fallen magische Effekte vom Himmel.
�
Verlassen Sie sich beim Programmieren nicht auf Defaulting,
sondern geben Sie die Typen Ihrer Funktionen an!
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
333
10 · Typklassen und Overloading
10.6
�
Die Typklasse Show · 10.6
Die Typklasse Show
Die Überführung ihrer Werte in eine externe Repräsentation (vom Typ
String) ist eine der Kernaufgaben jeder Programmiersprache, z.B. für
• Ein-/Ausgabe von Werten, interaktive Programme,
• Speichern/Laden/Übertragung von Daten.
�
In Haskell wird die benötige Funktionalität von Instanzen der
Typklasse Show bereitgestellt45 :
1
2
3
4
class Show α where
show :: α -> String
— Minimal complete definition: show
...
45 Typklasse
Read leistet die Umkehrabbildung, das sog. parsing.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
334
10 · Typklassen und Overloading
Beispiel
1
2
3
4
5
6
7
8
9
10
Die Typklasse Show · 10.6
Ausgabe von Werten des Typs Frac als Brüche der Form
x
.
y
showFrac :: Frac -> String
showFrac (x :/ y) = replicate (div (l-lx) 2) ' ' ++ sx ++ "\n" ++
replicate l '-'
++ "\n" ++
replicate (div (l-ly) 2) ' ' ++ sy
where
sx = show x
sy = show y
lx = length sx
ly = length sy
l = max lx ly
11
12
13
instance Show Frac where
show = showFrac
Haskell-Interpreter GHCi benutzt show :: Show α ⇒ α → String, um
in interaktiven Sessions Werte darzustellen:
1
2
3
4
*Main> 421 :/ 6546516
421
------6546516
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
335
10 · Typklassen und Overloading
Beispiel
1
2
3
4
5
6
7
8
9
10
11
Die Typklasse Show · 10.6
“Visualisierung” von Werten des Typs BinTree α .
showTree :: Show a => BinTree a -> String
showTree = concat . ppTree
where
ppTree :: Show a => BinTree a -> [String]
ppTree Empty
= ["@\n"]
ppTree (Node l x r) = [show x ++ "\n"]
++ ("|--" ++ ls) : map ("|
++ ("‘--" ++ rs) : map ("
where
ls:lss = ppTree l
rs:rss = ppTree r
" ++) lss
" ++) rss
12
13
14
1
2
3
4
5
6
7
8
9
10
instance Show a => Show (BinTree a) where
show = showTree
*Main> Node (leaf 1) 2 (Node (leaf 3) 4 Empty)
2
|--1
| |--@
| ‘--@
‘--4
|--3
| |--@
| ‘--@
‘--@
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
336
Herunterladen