Programmieren in Haskell - Typ

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