Grundlagen der Programmierung 2 (3A) Typen und Typcheck Prof. Dr. Manfred Schmidt-Schauÿ Künstliche Intelligenz und Softwaretechnologie 6. Mai 2009 Haskell, Typen, und Typberechnung Ziele: • Haskells Typisierung • Typisierungs-Regeln • Typ-Berechnung • Zutaten zu: Milners Typcheck Grundlagen der Programmierung 2 (3A) - 1 - Typisierung in Haskell Haskell hat eine starke, statische Typisierung. • jeder Ausdruck muss einen Typ haben • Der Typchecker berechnet Typen aller Ausdrücke (auch von Funktionen!) und prüft Typen zur Compilezeit • die Theorie sagt: Es gibt keine Typfehler zur Laufzeit d.h. kein dynamischer Typcheck nötig. Grundlagen der Programmierung 2 (3A) - 2 - Typisierung: sprachen) Monomorphe Typisierung Konsequenz: Begriffe (andere Programmier- Alle Objekte, Funktionen haben einen eindeutigen Typ Typvariablen gibt es nicht pro Typ der Listen-Argumente braucht man eine extra Längenfunktionen Polymorphe Typisierung Funktion arbeitet auf mehreren Daten-Typen Typvariablen: Mittel zur Schematisierung. parametrischer Polymorphismus. Konsequenz: Eine einzige Längenfunktion für alle Listen reicht aus Ist auch in Java 5 für generische Typen anwendbar Grundlagen der Programmierung 2 (3A) - 3 - Bemerkungen zu anderen Programmiersprachen Verwendung der arithmetischen Operatoren und Überladung von +, ∗, . . . Manche Programmiersprachen nutzen automatische (dynamische) Typkonversion Haskell: keine Typkonversion eingegebene Zahlkonstanten sind überladen eine eingegebene 8 wird vom Compiler (fromInteger 8) eingefügt als Typkonversion von Hand einfügen: Z.B. pi + 2%3 ergibt einen Typfehler, pi + (fromRational (2%3)) ergibt 3.80825932::Double Grundlagen der Programmierung 2 (3A) - 4 - Typisierung, Begriffe polymorph: Ein Ausdruck kann viele Typen haben (vielgestaltig, polymorph). parametrisch Die (i.a. unendliche) Menge der Typen entspricht eipolymorph: nem schematischen Typausdruck Beispiel Schema: [a]->Int Instanzen: [Int]->Int, Grundlagen der Programmierung 2 (3A) [Char]->Int, [[Char]]->Int - 5 - Syntax von Typen in Haskell (ohne Typklassen) hTypi ::= hBasistypi | (hTypi) | hTypvariablei | hTypkonstruktorni{hTypi}n (n = Stelligkeit) hBasistypi ::= Int | Integer | Float | Rational | Char Grundlagen der Programmierung 2 (3A) - 6 - Syntax von Typen Typkonstruktoren können benutzerdefiniert sein (z.B. Baum a) Vordefinierte Liste Typkonstruktoren: Tupel Funktionen [·] (.,...,.) · →· (Stelligkeit 2, Infix) Konvention zu Funktionstypen mit → a→b→c→d bedeutet: Grundlagen der Programmierung 2 (3A) a → (b → (c → d)). - 7 - Bemerkungen und Wiederholung spezielle syntaktische Konventionen für Tupel und Listen. Beispiel: Typangabe • • • (Int, Char) Paar von Int und Char [Int] Liste von Ints t :: τ Basistypen nennt man elementare Typen Grundtyp: Typ ohne Typvariablen (auch monomorph) polymorph wenn der Typ Typvariablen enthält. Grundlagen der Programmierung 2 (3A) - 8 - Beispiele [1,2,3] :: [Int] map :: (a → b) → [a] → [b] (:) :: a → [a] → [a] length :: [a] → Int Grundlagen der Programmierung 2 (3A) - 9 - Interpretation der Typen length :: [a] → Int ∀a.length :: [a] → Int logische Formel: Interpretation: Für alle Typen a ist length eine Funktion, die vom Typ [a] → Int ist. Logisch gilt dann: ∀x.x :: [a] ⇒ (length x) :: Int Grundlagen der Programmierung 2 (3A) - 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 Grundlagen der Programmierung 2 (3A) , 2:: Int - 11 - 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 Grundlagen der Programmierung 2 (3A) - 12 - Erweiterung der Anwendungsregel auf polymorphe Typen Ziel: Anwendung der Typregel für z.B. length oder map Die Funktion γ ist eine Typsubstitution wenn sie Typen für Typvariablen einsetzt γ(τ ) nennt man Instanz von τ Beispiel Typsubstitution: Instanz: γ = {a 7→ Char, b 7→ Float} γ([a] → Int) = [Char] → Int Grundlagen der Programmierung 2 (3A) - 13 - Erweiterung der Anwendungsregel auf polymorphe Typen s :: σ → τ, t :: ρ und γ(σ) = γ(ρ) (s t) :: γ(τ ) Hierbei ist zu beachten: die Typvariablen in ρ müssen vorher umbenannt werden. Grundlagen der Programmierung 2 (3A) - 14 - Beispiel zur polymorphen Anwendungsregel Typ von (map quadrat) ? map :: (a → b) → [a] → [b] Instanziiere mit: ergibt: {a 7→ Int, b 7→ Int} map :: (Int → Int) → [Int] → [Int] Regelanwendung ergibt: map :: (Int → Int) → [Int] → [Int], quadrat :: (Int → Int) (map quadrat) :: [Int] → [Int] Grundlagen der Programmierung 2 (3A) - 15 - Polymorphe Anwendungsregel für n Argumente s :: σ1 → σ2 . . . → σn → τ, t1 :: ρ1, . . . , tn :: ρn und ∀i : γ(σi) = γ(ρi) (s t1 . . . tn) :: γ(τ ) Die Typvariablen in ρ1, . . . , ρn müssen vorher umbenannt werden. Beachte verwende möglichst allgemeines γ Grundlagen der Programmierung 2 (3A) - 16 - Wie Berechnet man die Typsubstitutionen? Unifikation: Berechnung der allgemeinsten Typsubstitution Unifikation wird benutzt im Typchecker ! Grundlagen der Programmierung 2 (3A) - 17 - Erweiterung der Anwendungsregel auf polymorphe Typen mit Unifikation s :: σ → τ, . t :: ρ und γ ist allgemeinster Unifikator von σ = ρ (s t) :: γ(τ ) Hierbei ist zu beachten: die Typvariablen in ρ müssen vorher umbenannt werden. Grundlagen der Programmierung 2 (3A) - 18 - Unifikation: Regelbasierter Algorithmus Man braucht 4 Regeln, die auf den Gleichungen E operieren: E: Menge von Typgleichungen, G: gelöste; der Form x 7→ t. Beachte; x sind Typvariablen, t sind Typen, f, g sind Typkonstruktoren Grundlagen der Programmierung 2 (3A) - 19 - Unifikation: Die Regeln Start mit G = ∅ und der Menge der Eingabegleichungen E • G; . {x = x} ∪ E G; E • . G; {t = x} ∪ E . G; {x = t} ∪ E • . G; {x = t} ∪ E G[t/x] ∪ {x 7→ t}; E[t/x] • G; Wenn t keine Variable ist Wenn x nicht in t . {(f s1 . . . sn) = (f t1 . . . tn)} ∪ E . . G; {s1 = t1, . . . , sn = tn)} ∪ E Ersetzung: Effekt: E[t/x]: alle Vorkommen von x werden durch t ersetzt G[t/x]: jedes y 7→ s wird durch y 7→ s[t/x] ersetzt Grundlagen der Programmierung 2 (3A) - 20 - Unifikation: Regelbasierter Algorithmus Fehlerabbruch, wenn: • . x = t in E, x 6= t und x kommt in t vor. • . (f (. . .) = g(. . .) kommt in E vor und f 6= g. Grundlagen der Programmierung 2 (3A) - 21 - Beispiel mit Typvariablen id x = x id :: a → a. Berechne Typ von (map id) map:: id:: (a → b) a0 → a0 → [a] → [b] Regelanwendung benötigt . Lösung von (a → b) = (a0 → a0) (mit Unifikation) . Unifikation von (a → b) = (a0 → a0) ergibt: G ∅; ∅; {a 7→ a0}; {a 7→ a0, b 7→ a0}; Grundlagen der Programmierung 2 (3A) E . {(a → b) = (a0 → a0)} . . {a = a0, b = a0} . {b = a0} ∅ - 22 - Beispiel mit Typvariablen map :: (a → b) → ([a] → [b]), id :: a0 → a0 (map id) :: γ([a] → [b]) . . Einsetzung der Lösung {a = a0, b = a} ergibt (map id) :: ([a] → [a]). Grundlagen der Programmierung 2 (3A) - 23 - Beispiel zu Typberechnung Typ von map length map :: (a → b) → ([a] → [b]), length :: [a0] → Int (map length) :: ? . Unifiziere (a → b) = [a0] → Int G ∅; ∅; {a 7→ [a0]}; {a 7→ [a0], b 7→ Int}; Grundlagen der Programmierung 2 (3A) E . {(a → b) = ([a0] → Int)} . . {a = [a0], b = Int} . {b = Int} ∅ - 24 - Beispiel zu Typberechnung Typ von map length map :: (a → b) → ([a] → [b]), length :: [a0] → Int (map length) :: ? . Unifiziere (a → b) = [a0] → Int G ∅; ∅; {a 7→ [a0]}; {a 7→ [a0], b 7→ Int}; E . {(a → b) = ([a0] → Int)} . . {a = [a0], b = Int} . {b = Int} ∅ Somit: (map length) :: γ([a] → [b]) = [[a0]] → [Int] Grundlagen der Programmierung 2 (3A) - 25 - Beispiele zu polymorpher Typberechnung Berechne Typ der Liste [1]: 1. [1] = 1 : [] 2. 1 :: Int und [] :: [b] (Typen der Konstanten) 3. (:) :: a → [a] → [a] 4. Anwendungsregel mit γ = {a 7→ Int} ergibt: (1 :) :: [Int] → [Int] (mit Unifikation) 5. Anwendungsregel mit γ2 = {b 7→ Int} ergibt: (1 : []) :: [Int] (mit Unifikation) Grundlagen der Programmierung 2 (3A) - 26 - Beispiel zu Typfehler [1, ’a’] hat keinen Typ: • • • 1 : ( ’a’ : []) (voll geklammert) 1 :: Integer, [] :: [b], ’a’ :: Char Typen der Konstanten. (1 :) :: [Integer] → [Integer] und ’a’:[] :: [Char]. Kein Typ als Resultat, denn: [Integer] und [Char] sind verschieden unter allen γ. Grundlagen der Programmierung 2 (3A) - 27 - Beispiel zu Typfehler im Interpreter > [1,’a’] ERROR - Illegal Haskell 98 class constraint in inferred type *** Expression : [1,’a’] *** Type : Num Char => [Char] Im aktuellen Interpreter: No instance for (Num Char) arising from the literal ‘1’ at <interactive>:1:1 Possible fix: add an instance declaration for (Num Char) In the expression: 1 Grundlagen der Programmierung 2 (3A) - 28 - Typ eines Ausdrucks Typ von (map quadrat [1,2,3,4]) : • map:: (a → b) → [a] → [b] • quadrat:: Integer → Integer, und [1, 2, 3, 4] :: [Integer]. • γ= {a 7→ Integer, b 7→ Integer}. • Das ergibt: γ(a) = Integer, γ([a]) = [Integer], γ([b]) = [Integer]. • Resultat: γ([b]) = [Integer] Grundlagen der Programmierung 2 (3A) - 29 - Typisierung von Funktionen und Ausdrücken Problematik: rekursiv definierte Funktionen Lambda-Ausdrücke, let-Ausdrücke Listen-Komprehensionen. Typcheckalgorithmus von Robin Milner (in Haskell und ML) • • • ist schnell, liefert allgemeinste (Milner-)Typen benutzt Unifikation (eine optimierte Variante) • schlechte worst-case Komplexität: in seltenen Fällen exponentieller Platzbedarf Liefert in seltenen Fällen nicht die allgemeinsten möglichen polymorphen Typen • Grundlagen der Programmierung 2 (3A) - 30 - Typisierung von Funktionen, Milner Satz zur Milner-Typisierung in Haskell: Sei t ein getypter Haskell-Ausdruck, ohne freie Variablen. Dann wird die Auswertung des Ausdrucks t nicht mit einem Typfehler abbrechen. Allgemeine Aussage zu starken Typsystemen Keine dynamischen Typfehler, wenn das Programm korrekt getypt ist Grundlagen der Programmierung 2 (3A) - 31 - Typisierung und Reduktion: Zusammenhang 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. Grundlagen der Programmierung 2 (3A) - 32 - Typisierung und Reduktion: Zusammenhang ((λx.t) s) −→ t[s/x] Was ist der Typ von t[s/x]? Beh: ((λx.t) s) :: τ impliziert t[s/x] :: τ Argumentation: Folgerung: 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! Grundlagen der Programmierung 2 (3A) - 33 - Typisierung und Reduktion Beachte: Nach Reduktionen kann ein Ausdruck mehr Typen haben also vorher Beispiel: if 1 > 0 then [] else [1] :: [Integer] arithmetische-Reduktion: −→ if True then [] else [1] :: [Integer] Case-Reduktion: −→ [] :: [a] Grundlagen der Programmierung 2 (3A) - 34 -