13 Typinferenz

Werbung
13
Typinferenz
13 · Typinferenz
13.1
�
Typsysteme · 13.1
Typsysteme
Ziel der Typprüfung: alle Operanden von Operatoren haben einen
kompatiblen Typ.
• ... “Operatoren” sehr allgemein verstanden (z.B. auch Zuweisungen,
Funktionsaufrufe, ...)
�
Kompatibler Typ: Vom Operator erwarteter Typ.
• Manche Sprachen erlauben auch Typen, deren Werte automatisch in den
erwarteten Typ konvertiert werden können.
• Diese automatische Typkonvertierung nennt man Coercion, die zugehörigen
Regeln sind oft komplex50 .
�
Ein Typfehler besteht dann in der Anwendung eines Operators auf einen
Wert inkompatiblen Typs.
50 https://www.destroyallsoftware.com/talks/wat
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
401
13 · Typinferenz
Geschmacksrichtungen
Typsysteme · 13.1
tatsächlich ist diese Einteilung etwas schwammig
static/dynamic typing Ein statisches Typsystem überprüft die
Typkorrektheit beim Kompilieren (Haskell, Java, C).
• Zur Laufzeit liegen evtl. gar keine Typinformationen mehr vor (C).
• Ein dynamisches Typsystem prüft erst zur Laufzeit (Python).
strong/weak typing Ein starkes Typsystem erkennt alle Typfehler,
ggf. aber erst zur Laufzeit.
• Ein schwaches Typsystem kann bei unpassenden Typen zu unerwartetem
Verhalten führen (C), oder das Programm abbrechen.
polymorphic typing Polymorphie erlaubt die Verwendung einer Funktion
mit verschiedenen aber passenden Typen.
• Parametrische Polymorphie — Funktionen werden ohne Annahme über den
Typ des Argumentes implementiert.
• Ad-hoc Polymorphie — Für den jeweiligen Typ wird die passende
Implementierung einer Funktion ausgewählt.
• Subtyping — Funktionen für einen bestimmten Typ (z.B. Mammal) können
auch Werte von Untertypen (z.B. Bunny) verarbeiten.
Insgesamt sind Bezeichnungen wie statisch/dynamisch oder stark/schwach eher als
extreme Pole eines Spektrums von möglichen Ausprägungen zu verstehen.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
402
13 · Typinferenz
Typsysteme · 13.1
Programmierer profitieren von der Typprüfung ihrer Programme:
�
“Well-typed programs do not ‘go wrong’.” (Robin Milner): ein
typkorrektes Programm wendet Funktionen nur auf Werte an, für die sie
auch definiert wurden.
�
Viele ansonsten schwer zu entdeckende Fehler werden durch den
Type-Checker zur Compile-Zeit erkannt.
1
Prelude> foldl sqrt
2
3
4
<interactive>:2:7:
Occurs check: cannot construct the infinite type: b ~ a -> b
Aber auch der Compiler und die Laufzeitumgebung ziehen Vorteile aus
der Typisierung:
�
Für ein typkorrektes Programm muss das Laufzeitsystem der Sprache
keine Tests auf die Typkorrektheit ausführen (⇒ kürzere Laufzeiten).
�
Der Compiler muss entsprechend keinen Code für solche Tests generieren
(⇒ kompakterer Objekt-Code).
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
403
13 · Typinferenz
Typsysteme · 13.1
Überprüfung der Typkorrektheit
�
Type Checking
• Programmierer deklariert zu jedem Objekt (Variablen, Parameter, ...) den
gewünschten Typ.
• Compiler (statisch) bzw. Laufzeitsystem (dynamisch) prüft Typregeln und
meldet ggf. Typfehler.
�
Type Inference
• Programmierer gibt (fast) keine explizite Typisierung von Objekten an.
• Compiler leitet gewünschten Operandentyp aus Operatoren und
Inferenzregeln ab, meldet ggf. Typfehler.
Die meisten Programmiersprachen verwenden teilweise Typinferenz (z.B.
bei numerischen Konstanten), fordern aber auch Typdeklarationen, die
dann (statisch/dynamisch) geprüft werden.
Haskell basiert (fast ausschließlich) auf Typinferenz.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
404
13 · Typinferenz
13.2
�
�
Automatische Typinferenz · 13.2
Automatische Typinferenz
In Haskell ist es bis auf wenige Ausnahmefälle nicht notwendig (aber
sehr hilfreich), Werte und Ausdrücke mit ihren jeweiligen Typen zu
annotieren. Stattdessen leitet der Compiler die Typisierung
automatisch ab.
Die Typinferenz-Komponente eines Compilers
1. prüft, ob ein Programm nach den Typregeln der Sprache korrekt typisiert ist
(type check), und
1 erfüllt sein) leitet automatisch den Typ jedes Ausdrucks innerhalb
2. (sollte ○
dieses Programms ab (type inference).
�
Diese beiden Aufgaben werden verschränkt (nicht eine nach der anderen)
ausgeführt.
�
1 und
Mittels Intuition und “scharfem Hinsehen” lassen sich die Punkte ○
○
2 für viele Ausdrücke und Funktionsdefinitionen auch intuitiv ableiten.
Der später vorgestellte Inferenzalgorithmus orientiert sich an dieser
Intuition.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
405
13 · Typinferenz
Beispiel
Automatische Typinferenz · 13.2
Typinferenz für die Funktion foldr mit folgender Definition:
foldr f z = go
where go [ ]
= z
go (x : xs) = f x (go xs)
“Scharfes Hinsehen” liefert:
1. go operiert offensichtlich auf Listen und hat daher den Typ [α] → β.
2. Sowohl z als auch f x (go xs) können ein Ergebnis von go darstellen, und
haben also den Typ β.
3. Da x :: α und (go xs) :: β, muss f vom Typ α -> β -> β sein.
Also:
foldr :: (α → β → β) → β → [α] → β
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
406
13 · Typinferenz
Automatische Typinferenz · 13.2
Beobachtungen
Diese intuitive Typinferenz umfasst dabei die
1. Bestimmung eines Typs für einen Ausdruck an sich, wie etwa im letzten
Beispiel für go. Im Allgemeinen wird dieser Typausdruck Typvariablen
beinhalten.
2. Bestimmung eines Typs für einen Teilausdruck bereits typisierter
Ausdrücke, wie eben für z und f geschehen.
Typkorrekt Soll der Gesamtausdruck typkorrekt sein, dürfen sich die
dabei abgeleiteten Typen nicht widersprechen, d.h. sie müssen durch
geeignete Substitution von Typvariablen in dieselbe Form gebracht werden
können.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
407
13 · Typinferenz
Automatische Typinferenz · 13.2
Kernsprache für Typinferenz
Um den Rahmen der Betrachtungen nicht zu sprengen, besprechen wir hier
einen Typinferenz-Algorithmus für einen erweiterten λ-Kalkül:
�
Wir erweitern den einfachen λ-Kalkül um Konstanten (cf. Seite 94) und
lokale Definitionen via let ... in , und erhalten
Expr →
|
|
|
|
Const
Var
Expr Expr
λVar. Expr
let Var = Expr in Expr
Konstanten
Variablen
Applikation
λ-Abstraktion
lokale Definition
• Der let-Ausdruck kann hier tatsächlich nur eine Variable binden, erlaubt
uns aber immerhin Rekursion einzuführen.
• Üblicherweise wird ein mächtigeres let verwendet, das z.B. auch wechselseitig
rekursive Definitionen zulässt.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
408
13 · Typinferenz
13.3
�
Inferenzregeln · 13.3
Inferenzregeln
Inferenzregeln werden in der Logik oft in der Form
Prämisse1
Prämisse2
Prämisse3
Folgerung
verwendete Regel
notiert.
• Auf dieser Notation bauen z.B. Systeme natürlichen Schließens auf, eine
Familie von Kalkuli aus der Logik.
�
Damit lassen sich ganze Beweise elegant als Inferenzbäume
aufschreiben51 :
X ⇒ Y
X
Y
⇒B
Y ∧Z
�
Z
∧E
Angelehnt an diese Notation werden wir die Regeln der Typinferenz
notieren.
51 Dabei
stehen B bzw. E für Beseitigungs- bzw. Einfügeregeln.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
409
13 · Typinferenz
13.4
Regeln für die Typinferenz · 13.4
Regeln für die Typinferenz
Applikation & Abstraktion
�
In diesem Kapitel stehen die Ti für noch unbekannte Typen (i ∈ N).
Die Typvariablen (α, β, ...) verwenden wir dann für polymorphe Typen.
�
Seien f , e beliebige λ-Ausdrücke; x eine Variable.
Applikation
Die Inferenzregel für die Funktionsapplikation f e lautet:
f :: T1 → T2
e :: T1
f e :: T2
@
�
In einer Applikation f e muss f einen Typ haben, der den Typ T1 des
Arguments auf einen Ergebnistyp T2 abbildet.
�
T2 ist dann der Typ des Ausdrucks f e.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
410
13 · Typinferenz
Regeln für die Typinferenz · 13.4
Abstraktion Für die Abstraktion λx. e lautet die Regel:
e :: T2
λx. e :: T1 → T2
λx :: T1
�
Unter der Annahme52 daß die alle in e freien x den Typ T1 haben, ist
e :: T2 .
�
Die Funktion λx. e liefert dann für ein Argument vom Typ T1 Werte vom
Typ T2 des Funktionsrumpfes e.
52 diese
notieren wir etwas unorthodox rechts neben dem Bruchstrich.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
411
13 · Typinferenz
Regeln für die Typinferenz · 13.4
Beispiel
�
Bestimme den Typ T0 von const = λx. λy . x:
Für eine Abstraktion λx... verwenden wir immer die λ-Regel:
λy . x :: T2
λx. λy . x :: T0
• Daraus lernen wir schon:
λx :: T1
T 0 = T1 → T 2 ,
• allerdings bleibt die Frage was T2 für
ein Typ sein soll.
�
Auch dafür wenden wir die λ-Regel an, und schreiben sie darüber:
x :: T1
• Dabei wissen wir schon aus der
λy :: T3
unteren Regel, daß x den Typ T1
λy . x :: T2
haben muss!
λx :: T1
λx. λy . x :: T0
• Neu lernen wir T = T → T .
2
�
3
1
Einsetzen der Gleichung für T2 in die Gleichung für T0 liefert:
T 0 = T1 → T 3 → T 1
�
Diese Erkenntnis gilt offenbar für alle Typen T1 und T3 :
const :: ∀α β. α → β → α
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
412
13 · Typinferenz
Regeln für die Typinferenz · 13.4
Notation
�
Es ist nicht nötig die einzelnen Teilausdrücke bei jedem Schritt
hinzuschreiben:
x :: T1
T2
T0
λy :: T3
λx :: T1
x :: T1
statt
λy . x :: T2
λy :: T3
λx. λy . x :: T0
λx :: T1
• An der Struktur des Inferenzbaumes lässt sich die Struktur des Ausdrucks
ablesen — es ist der AST mit der Wurzel unten.
�
Analog zu λx y z. e = λx. λy . λz. e fassen wir Abstraktionen oft
zusammen: Für λx y . x also
x :: T1
λy :: T3
x :: T1
statt
λx :: T1 , y :: T2
T2
T0
λx :: T1
T0
• Daraus lesen wir direkt ab: T0 = T1 → T2 → T1 .
• Offensichtlich sparen wir uns eine Typvariable — und auch Rechenarbeit.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
413
13 · Typinferenz
Beispiel
Regeln für die Typinferenz · 13.4
Typisierung von λx f . f x:
1. Zuerst konstruieren wir den Inferenzbaum:
f :: T2
x :: T1
T3
•
�
T0
(rechts der AST zum Vergleich)
x
f
@
@
λx :: T1 , f :: T2
λf
λx
Wichtig: An alle durch das gleiche Lambda gebundenen Variablen die
gleiche Typvariable schreiben!
2. Dann fangen wir unten (bei der Wurzel T0 ) an, Gleichungen abzulesen:
T 0 = T 1 → T2 → T3
T 2 = T 1 → T3
aus der λ-Regel
aus der @-Regel
3. Einsetzen in T0 liefert: T0 = T1 → (T1 → T3 ) → T3 .
4. Allquantifizieren liefert: λx f . f x :: ∀α β. α → (α → β) → β.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
414
13 · Typinferenz
Regeln für die Typinferenz · 13.4
Beispiel Die Typisierung von S = λf g x. f x (g x):
Wir konstruieren den Inferenzbaum
(rechts nochmal der AST zum Vergleich)
�
f :: T1
x :: T3
T5
@
T4
T0
�
lesen ab
�
setzen ein
�
g :: T2
T6
x :: T3
g
x
f
x
@
@
@
@
@
λf :: T1 , g :: T2 , x :: T3
λf g x
T0 = T 1
T5 = T 6
T1 = T 3
T2 = T 3
→ T2 → T3 → T 4
→ T4
→ T5
→ T6
T0 = (T3 → T5 ) → (T3 → T6 ) → T3 → T4
= (T3 → T6 → T4 ) → (T3 → T6 ) → T3 → T4
und allquantifizieren:
Stefan Klinger · DBIS
S :: ∀α β γ. (α → β → γ) → (α → β) → α → γ
Informatik 2 · Sommer 2016
415
13 · Typinferenz
Beispiel
Regeln für die Typinferenz · 13.4
Innere Abstraktion und Namensüberdeckung: λx y . x (λx. x y )
x :: T5
y :: T2
T6
x :: T1
�
�
T4
T3
T0
@
λx :: T5
@
liefert
λx :: T1 , y :: T2
T0 = T 1
T1 = T 4
T4 = T 5
T5 = T 2
→ T2 → T3
→ T3
→ T6
→ T6
Wo genau kommen die Typen Ti der einzelnen Variablen her?
Dann nur noch einsetzen und allquantifizieren:
T0 = ... = (((T2 → T6 ) → T6 ) → T3 ) → T2 → T3
λx y . x (λx. x y ) :: ∀α β γ. (((α → β) → β) → γ) → α → γ
1
2
3
Prelude> :t \x y -> x (\x -> x y)
— Man kann ja mal fragen
\x y -> x (\x -> x y) :: (((r2 -> r1) -> r1) -> r) -> r2 -> r
— Eigentlich: ∀r r1 r2...., der GHC lässt den Allquantor leider weg.
Stefan Klinger · DBIS
Informatik 2 · Sommer 2016
416
Herunterladen