Haskell, Typen, und Objektorientierung ZIELE dieses Kapitels Haskells Typisierung Milners Polymorpher Typcheck Haskells Typklassen P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 1 Typisierung in Haskell Haskell hat eine starke, statische Typisierung. • • jeder Ausdruck muss einen Typ haben Typchecker berechnet Typen und prüft Typen zur Compilezeit • die Theorie sagt: Es gibt keine Typfehler zur Laufzeit d.h. kein dynamischer Typcheck nötig. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 2 Typisierung: Begriffe (andere Programmiersprachen) dynamische Typisierung passiert zur Laufzeit. Datenobjekte haben einen Typ zur Laufzeit. bei falschem Aufruf: Laufzeit-Typfehler. schwache, statische Typisierung Typcheck zur Kompilierzeit Aber man kann Typcheck umgehen deshalb: dynamischer Typcheck notwendig. Laufzeit Typfehler sind möglich Konstanten, Variablen (Bezeichner), Prozeduren und Funktionen haben einen im Programm festgelegten Typ. Typisch: Typen können zur Laufzeit per Programmbefehl abgefragt werden P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 3 Typisierung: Begriffe (andere Programmiersprachen) Monomorphe Typisierung Alle Objekte, Funktionen haben einen eindeutigen Typ Typvariablen sind verboten Konsequenz: verschiedene Längenfunktionen für Listen von unterschiedlichem Typ Polymorphe Typisierung Funktion arbeitet auf mehreren Daten-Typen Typvariablen: Mittel zur Schematisierung. parametrischer Polymorphismus. Eine einzige Längenfunktion für alle Listen reicht aus Ist auch in Java 5 für generische Typen anwendbar P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 4 Typisierung, Begriffe polymorph: Ein Ausdruck kann viele Typen haben (vielgestaltig, polymorph). Die (teilweise unendliche) Menge der Typen entspricht einem schematischen Typausdruck [α]->Int ist das Schema Instanzen sind z.B. [Int]->Int, [Char]->Int, P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 5 [[Char]]->Int Syntax von Typen in Haskell (ohne Typklassen) hTypi ::= hBasistypi | (hTypi) | hTypvariablei | hTypkonstruktorni{hTypi}n (n = Stelligkeit) hBasistypi ::= Int | Integer | Float | Rational | Char P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 6 Syntax von Typen Typkonstruktoren können benutzerdefiniert sein (z.B. Baum α) Vordefinierte Liste Typkonstruktoren: Tupel Funktionen [·] (.,...,.) · → · (Stelligkeit 2, Infix) Konvention zu Funktionstypen mit → a→b→c→d P raktische Inf ormatik 2, SS bedeutet: 2005, F olien Kap.3, (27. M ai2005) a → (b → (c → d)). Seite 7 Bemerkungen und Wiederholung spezielle syntaktische Konventionen für Tupel und Listen. Beispiel: (Int, Char) [Int] Typangabe • • • t :: τ Basistypen nennt man elementare Typen Grundtyp: Typ ohne Typvariablen (auch monomorph) polymorph wenn der Typ Typvariablen enthält. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 8 Beispiele [1,2,3] :: [Int] map :: (α → β) → [α] → [β] (:) :: α → [α] → [α] length :: [α] → Int P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 9 Interpretation der Typen length :: [α] → Int ∀α.length :: [α] → Int logische Formel: Interpretation: Für alle Typen α ist length eine Funktion, die vom Typ [α] → Int ist. Logisch gilt dann: ∀x.x :: [α] ⇒ (length x) :: Int P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 10 Typregeln Wie berechnet man Typen von Ausdrücken? Anwendung von Funktionen auf Argumente (σ, τ sind Typen) s :: σ → τ , t :: σ (s t) :: τ Beispiele: quadrat :: Int → Int , 2 : Int (quadrat 2) :: Int + :: Int → (Int → Int) , 1 :: Int (1 +) :: (Int → Int) ((1 +) 2) :: Int P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 11 , 2 :: Int Typregel Anwendung: mehrere Argumente s :: σ1 → σ2 . . . → σn → τ , t1 :: σ1 , . . . , tn :: σn (s t1 . . . tn) :: τ Beispiele + :: Int → Int → Int, 1 :: Int , 2 :: Int (+ 1 2) :: Int + :: Int → Int → Int (1 + 2) :: Int P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 12 Erweiterung der Anwendungsregel auf polymorphe Typen Ziel: Anwendung der Typregel für length oder map Die Funktion γ ist eine Typsubstitution wenn sie Typen für Typvariablen einsetzt γ(τ ) nennt man Instanz von τ Beispiel Typsubstitution: Instanz: P raktische Inf ormatik 2, SS γ = {α 7→ Char, β 7→ Float} γ([α] → Int) = [Char] → Int 2005, F olien Kap.3, (27. M ai2005) Seite 13 Erweiterung der Anwendungsregel auf polymorphe Typen s : σ → τ, t : ρ und γ(σ) = γ(ρ) (s t) :: γ(τ ) Hierbei: die Typvariablen in ρ müssen vorher umbenannt werden. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 14 Beispiel zur polymorphen Anwendungsregel Typ von (map quadrat) ? map :: (α → β) → [α] → [β] Instanziiere mit: ergibt: {α 7→ Int, β 7→ Int} map :: (Int → Int) → [Int] → [Int] Regelanwendung ergibt: map : (Int → Int) → [Int] → [Int], quadrat :: (Int → Int) (map quadrat) :: [Int] → [Int] P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 15 Polymorphe Anwendungsregel für n Argumente s :: σ1 → σ2 . . . → σn → τ, (s t1 : ρ1, . . . , tn : ρn und ∀i : γ(σi) = γ(ρi) t1 . . . tn) :: γ(τ ) Die Typvariablen in ρ1, . . . , ρn müssen vorher umbenannt werden. Beachte verwende möglichst allgemeines γ P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 16 Beispiel mit Typvariablen id :: α → α. id x = x Berechne Typ von (map id) map:: id:: (α → β) α0 → α0 → [α] → [β] Regelanwendung: map : (α → β) → ([α] → [β]), id :: α0 → α0 (map id) :: γ([α] → [β]) d.h. (map id) :: ([α] → [α]). P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 17 Berechnung von Typsubstitutionen, die Typen gleichmachen Gegeben: δi, ρi, i = 1, . . . , n Gesucht: γ mit ∀i : γ(δi) = γ(ρi). Die Berechnung mit den folgenden Regeln nennt man auch Unifikation. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 18 Unifikation durch Transformation der Gleichungen Notation: σ =γ τ statt γ(σ) = γ(τ ) Sei G eine Multimenge von Gleichungen, T C ein Typkonstruktor, α eine Typvariable, σ, τ (polymorphe) Typen. Regeln zur Umformung von Gleichungssystemen: (Dekomposition) (T C σ1 . . . σm) =γ (T C τ1 . . . τm), G σ1 =γ τ1, . . . , σm =γ τn, G Wenn die Typkonstruktoren rechts und links verschieden sind, dann kann man die Berechnung abbrechen; es kann keine Lösung geben. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 19 Unifikation durch Transformation der Gleichungen: Regeln (Ersetzung) α =γ σ, G α =γ σ, G[σ/α] G[σ/α] bedeutet: Ersetze alle Vorkommen der Typvariablen α durch den Typ σ. Hierbei: Grund: P raktische Inf ormatik σ darf α nicht enthalten Es gibt keine Lösung Vorsicht: sonst Nichtterminierung 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 20 Unifikation durch Transformation der Gleichungen: Regeln (Vereinfachung) α =γ α, G G σ =γ τ, G (Vertauschung) τ =γ σ, G P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 21 Unifikation durch Transformation der Gleichungen: Resultat Berechnung ist erfolgreich beendet, wenn das Gleichungssystem von der Form ist: α1 =γ τ1, . . . , αk =γ τk wobei αi Typvariablen sind, und die αi in keinem τj auftreten. Die gesuchte Typsubstitution ist ablesbar als γ = {α1 7→ τ1, . . . , αk 7→ τk } P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 22 Beispiel map id nochmal; Variante 1 Typsubstitutionen unterschiedlich, je nach Regelanwendung: map :: (α → β) → [α] → [β], id :: α0 → α0 (map id) :: γ([α] → [β]) Berechnen von γ: α → β =γ α 0 → α 0 α =γ α 0 , β =γ α 0 Das ergibt: γ = {α 7→ α0, β 7→ α0} und (map id) :: [α0] → [α0] P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 23 Beispiel map id Variante 2 Berechnung von γ mit vertauschten Regelanwendungen: α → β =γ α 0 → α 0 α =γ α 0 , β =γ α 0 α0 =γ α, β =γ α0 α0 =γ α, β =γ α Das ergibt γ = {α0 7→ α, β 7→ α} und (map id) :: [α] → [α] Wegen All-Quantifizierung sind die Typen (bis auf Umbenennung) identisch P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 24 Typsubstitutionen: Allgemeinere Definition Sei V eine Menge von Typvariablen. und γ, γ 0 zwei Typsubstitutionen. Dann ist γ allgemeiner als γ 0, wenn es eine weitere Typsubstitution δ gibt, so dass ∀x ∈ V : δ(γ(x)) = γ 0(x) Es gilt: Unifikation berechnet allgemeinste Typsubstitutionen P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 25 Beispiel zu Typsubstitutionen γ = {α1 → 7 (α2, β2)} γ 0 = {α1 → 7 (Int, Int)} γ ist allgemeiner als γ 0: Nehme δ = {α2 7→ Int, β2 7→ Int}. Dann ist δ(γ(α1)) = (Int, Int) = γ 0(α1). P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 26 Beispiel zu polymorpher Typberechnung Berechne Typ der Liste [1]: • [1] = 1 : [] • 1 :: Int und [] :: [β] (Typen der Konstanten) • (:) :: α → [α] → [α] • Anwendungsregel mit γ = {α 7→ Int} ergibt: (1 :) :: [Int] → [Int] • Anwendungsregel mit γ2 = {β 7→ Int} ergibt: (1 : []) :: [Int] P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 27 Beispiel zu Typfehler [1, 0a0] hat keinen Typ: • • • 1 : (0a0 : []) (voll geklammert) 1 :: Integer, [] :: [β], 0a0 :: Char Typen der Konstanten. (1 :) :: [Integer] → [Integer] und ’a’:[] :: [Char]. Kein Typ als Resultat, denn: [Integer] und [Char] sind verschieden unter allen γ. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 28 Beispiel zu Typfehler;Hugs Test > [1,’a’] ERROR - Illegal Haskell 98 class constraint in inferred type *** Expression : [1,’a’] *** Type : Num Char => [Char] P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 29 Typ eines Ausdrucks Typ von (map quadrat [1,2,3,4]) : • map:: (α → β) → [α] → [β] • quadrat:: Int → Integer, und [1, 2, 3, 4] :: [Integer]. • γ= {α 7→ Integer, β 7→ Integer}. • Das ergibt: γ(α) = Integer, γ([α]) = [Integer], γ([β]) = [Integer]. • Resultat: γ([β]) = [Integer] P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 30 Typisierung von Funktionen, Milner Problematik: rekursiv definierte Funktionen Lambda-Ausdrücke, let-Ausdrücke Listen-Komprehensionen. Typcheckalgorithmus von Robin Milner (in Haskell und ML) Schnell, liefert allgemeinste (Milner-)Typen schlechte worst-case Komplexität: in seltenen Fällen exponentieller Platzbedarf Liefert in seltenen Fällen nicht die allgemeinsten möglichen polymorphen Typen P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 31 Typisierung von Funktionen, Milner Satz zur Milner-Typisierung in Haskell: Sei t ein getypter Haskell-Ausdruck, ohne freie Variablen (d.h. geschlossen). Dann wird die Auswertung des Ausdrucks t nicht mit einem Typfehler abbrechen. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 32 Typisierung rekursiv definierter Funktionen length [] length (x:xs) = = 0 (1 + length xs) der erhaltene Typ ist Num a => [b] -> a Das ist auch der Typ von genericLength im Modul List P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 33 Typisierung rekursiv definierter Funktionen Vorgehen bei der Berechnung des Typs einer rekursiven Funktion f x_1 ... x_n = ... • • • • • Annahmen:f:: α x 1:: β1 . . . x n:: βn, Resultat: βn+1, Ergibt Typgleichung α = β1-> . . . ->βn->βn+1 bei Pattern verwende extra Gleichungen x i = p i Typregeln zum Berechnen der Typen der Definitionsgleichung Bei bekannten Funktionen: verwende umbenannten Typ. Bei f: verwende α Lösung von Typgleichungen mittels Unifikationsalgorithmus Gleichsetzen der Typen der linken und rechten Seiten von Definitionsgleichungen Resultat: γ(β1-> . . . ->βn->βn+1) wobei γ die insgesamt berechnete Typsubstitution ist. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 34 Typisierung rekursiv definierter Funktionen (2) [] ++ (x:xs) ++ P raktische Inf ormatik ys ys 2, SS = = 2005, F olien Kap.3, ys x: (xs++ys) (27. M ai2005) Seite 35 Typisierung rekursiv definierter Funktionen (2) [] ++ (x:xs) ++ ys ys = = ys x: (xs++ys) Annahmen: x:xs:: β1, ys:: β2 [] ++ ys = ys (x:xs) (x:xs) ++ ys (xs++ys) x: (xs++ys) α = β1 → β2 → β3 β1 = [α1], β3 = β2 x:: α2, xs:: α3, ergibt: α3 = [α2] = β1 [α1] = [α2], also α1 = α2 :: β2 keine neue Gleichung; β2 = [α1] Erst hier wird der Resultattyp eingeschränkt! Ergebnis: ++:: [α1] → [α1] → [α1] P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 36 Typisierung und Reduktion, genauer Erinnerung Beta-Reduktion: ((λx.t) s) −→ t[s/x] λx.t :: τ1 → τ2 , s :: σ , γ(τ1) = γ(σ) ((λx.t) s) : γ(τ2) Zum Ermitteln von γ(τ2) benötigt man eine Typsubstitution γ, so dass γ(τ1) = γ(σ) ist. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 37 Typisierung und Reduktion, genauer ((λx.t) s) −→ t[s/x] Was ist der Typ von t[s/x]? Beh: ((λx.t) s) :: τ impliziert t[s/x] :: τ Argumentation: Folgerung: P raktische Inf ormatik 2, SS Milner-Typcheck beachtet nur den Typ der Unterterme, nicht deren syntaktische Form. Typ von x und s ist aber gleich (als Unterterm von t) denn γ(τ1) = γ(σ) auch mehrere Beta-Reduktionen erhalten den ursprünglichen Typ Analoge Argumentation für andere Auswertungsregeln! 2005, F olien Kap.3, (27. M ai2005) Seite 38 Typisierung und Reduktion, genauer Beachte: Nach Beta-Reduktion kann ein Ausdruck mehr Typen haben Beispiel: (((λx.(λy.x)) 1) u) Beta-Reduktion: −→ (λy.1) u Beta-Reduktion: −→ 1 P raktische Inf ormatik 2, SS 2005, F olien Kap.3, u ungetypt. Deshalb ist der Ausdruck ungetypt. ungetypt getypt; vom Typ Integer (27. M ai2005) Seite 39 Bemerkungen zu anderen Programmiersprachen Lisp und Scheme (strikte funktionale Programmiersprachen) haben kein statisches Typsystem. der Grund liegt in der Konzeption, z.B. der Booleschen Werte In Lisp: Nil bedeutet auch False, alles andere gilt als True. Auswertung: 1 = 0 → Nil, 1 = 1 −→ 1. Weitere Schwierigkeit: Typeffekte von Zuweisungen P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 40 Bemerkungen zu anderen Programmiersprachen Verwendung der arithmetischen Operatoren Überladung von +, ∗, . . . Manche Programmiersprachen nutzen automatische (dynamische) Typkonversion Haskell: keine Typkonversion eingegebene Zahlkonstanten sind überladen 1 wird vom Compiler als (fromInteger 1) eingefügt Typkonversion von Hand einfügen: Z.B. pi + 2%3 ergibt einen Typfehler, pi + (fromRational (2%3)) 3.80825932::Double P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 41 ergibt Python und Typisierung Python ist statisch ungetypt. Vermutlich wäre ein monomorphes Typsystem für Python möglich Man muss syntaktische Elemente hinzufügen Schwierige Kombination: Python erlaubt Lambda-Ausdrücke und Zuweisung P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 42 Java und Typisierung Java 1.4 ist streng getypt. Das Typsystem ist monomorph. die Java-Klassen entsprechen den elementaren Typen. Zusätzlich gibt es einen Untertyp-Begriff für Klassen. Die dynamischen Typfehler sind meist (cast)-Fehler. Java 5 ist streng getypt. Das Typsystem ist polymorph. Zusätzlich gibt es einen Untertyp-Begriff für Klassen. Kompliziertere statische Typangaben nötig Weniger dynamische Typfehler. P raktische Inf ormatik 2, SS 2005, F olien Kap.3, (27. M ai2005) Seite 43