Kapitel 6: Typinferenz - Theoretische Informatik

Werbung
Kapitel 6: Typinferenz
Andreas Abel
LFE Theoretische Informatik
Institut für Informatik
Ludwig-Maximilians-Universität München
6. Juni 2011
Quelle: Martin Wirsing, Typüberprüfung und Typinferenz
Foliensatz ProMo SS 2010
Andreas Abel (LMU)
Typinferenz
06.06.11
1 / 27
Einführung
Einführende Fragen
Wozu dient das SML-Typsystem?
Welche Vorteile haben getypte Sprachen gegenüber ungetypten?
Wie bestimmt man den Typ eines SML-Programmes? Z.B.
fn x => fn f => f x
(fn x => x 0) 1
fn f => fn x => f x + f 1.0
fn x => x x
fn x => fn f => x (f x)
Inhalt:
Typisierungsregeln
Typsubstitutionen und Unifikation
Typinferenz
Andreas Abel (LMU)
Typinferenz
06.06.11
2 / 27
Einführung
Typfehler zur Laufzeit?
Was passiert bei der Auswertung von (fn x => x 0) 1?
Keine Typprüfung: 1 wird als Sprungaddresse interpretiert.
Systemabsturz oder schwere Betriebssystem-Ausnahme!
Dynamische Typprüfung: Werte tragen zur Laufzeit
Typ-Markierung Ganzzahl, Funktion, . . . Da 1 keine Funktion ist, wird
eine Ausnahme im Laufzeitsystem geworfen.
Nachteil: Vor jeder Operation müssen Typen geprüft werden!
Statische Typprüfung: Der Übersetzer weigert sich, ein Programm
mit Typfehlern zu übersetzen. Es kommt nie zu derartigen
Laufzeitfehlern.
Robin Milner (1934-2010): Well-typed programs do not go wrong.
Der Übersetzer findet bereits viele Programmierfehler.
Andreas Abel (LMU)
Typinferenz
06.06.11
3 / 27
Einführung
Typsysteme in der Praxis
Keine Typprüfung: Assembler
Dynamische Typprüfung: LISP, Scheme, BASIC
Skriptsprachen: JavaScript, Python
Statische Typprüfung: C (unsicher), JAVA (teilw. dynamisch), Scala,
SML, Ocaml, Haskell, Agda.
Erfahrung mit statisch getypten funktionalen Sprachen:
If it type-checks, it works.
Andreas Abel (LMU)
Typinferenz
06.06.11
4 / 27
Ausdrücke
Ausdrücke (abstrakte Syntax)
Wir betrachten Programme e im funktionalen Kern von SML, dem
λ-Kalkül (1936, Alonzo Church(1902-95)):
e ::=
|
|
|
c
x
e e0
fn x ⇒ e
Konstante
Variable
Anwendung
Funktionsabstraktion
2.718
xdiff
Math.sqrt 5.0
fn xd => xd - 1
Eine Konstante c ist z.B. eine Ganz- oder Fließkommazahl, eine
Zeichenkette, oder eine Operation.
c ::=
|
|
|
n
r
s
f
Ganzzahl
Fließkommazahl
Zeichenkette
Operation
0, 1, ~1
0.01, 2.0
"Anton", ""
+, -, o, @
Infix-Operatoren 5 + 3 schreiben wir präfix + 5 3 in abstrakter
Syntax.
Andreas Abel (LMU)
Typinferenz
06.06.11
5 / 27
Ausdrücke
Typausdrücke und Typisierungskontexte
Wir beschränken uns auf Grund- und Funktionstypen.
Grammatik für die abstrakte Syntax von Typen:
A, B ::= α | β | . . .
| int | real | string | . . .
| A→B
Typvariable
’a, ’b
Grundtyp
Funktionstyp int -> ’a
Klammern sind in der Grammatik nicht erwähnt, sie dienen nur der
Disambiguierung.
A → B → C = A → (B → C ) 6= (A → B) → C
Andreas Abel (LMU)
Typinferenz
06.06.11
6 / 27
Typisierung
Typzuweisung
Eine Typzuweisung e : A ist ein Paar aus einem SML-Ausdruck e und
einen SML-Typen A, interpretiert als Aussage “e hat Typ A”.
4
true
3.3
fn x
fn x
fn x
z (x
:
:
:
=> x - 1
:
=> fn y => x :
=> fn y => y :
+ 0)
:
int
wahr
string
falsch
’a
falsch
int -> int
wahr
’a -> ’b -> ’a wahr
’a -> ’b -> ’a falsch
bool
???
Der Typausdruck z (x + 0) enthält freie Variablen x und z.
Wir benötigen eine Typzuweisung für die freien Variablen!
Andreas Abel (LMU)
Typinferenz
06.06.11
7 / 27
Typisierung
Typisierungskontext
Eine endliche Menge Γ = {x1 :A1 , . . . xn :An } (n ≥ 0) von
Typzuweisungen an paarweise verschiedene Variablen x1 , . . . , xn
heißt Typisierungskontext (engl. typing context/environment).
{b : bool, half : int → int}
{z : int → bool, x : int}
{h : int → bool, h : int}
gültig
gültig
ungültig
Γ ist eine endliche Abbildung von Variablen x auf Typen Γ(x).
Wir schreiben Γ, x:A für das Einfügen der Typzuweisung x:A in Γ.
Eine schon vorhandene Typzuweisung für x wird überschrieben.
Bsp.: {h : int → bool}, h : int = {h : int}.
Andreas Abel (LMU)
Typinferenz
06.06.11
8 / 27
Typisierung
Typisierung
Ein Typ(isierungs)urteil (engl: typing judgement) ist eine Aussage
der Form Γ ` e : A .
Bedeutung: “Unter der Annahme, dass die Variablen die in Γ
angegebenen Typen haben, hat e den Typ A”.
Kurz: “Im Kontext Γ hat e den Typ A”.
Formal ist ` : eine dreistellige Relation zwischen Typkontexten,
Ausdrücken, und Typen.
{y : int}
` fn x ⇒ + y (* 2 x) : int → int gültig
{x : ’a}
` fn y ⇒ x
: ’b → ’a
gültig
{}
` fn x ⇒ + x x
: ’a → ’a
ungültig
Andreas Abel (LMU)
Typinferenz
06.06.11
9 / 27
Typisierung
Typregeln
Gültige Typurteile werden mittels Typregeln hergeleitet.
Eine Regel hat die Form P1 ∧ · · · ∧ Pn =⇒ K , d.h., aus den
Prämissen P1 . . . Pn folgt die Konklusion K , geschrieben
P1 . . . Pn
K
In unserem Fall sind die Pi und K Typurteile.
Regeln ohne Prämissen heißen Axiome.
Für jede Konstante c gibt es ein Typaxiom, z.B.
Γ `1
: int
Γ ` true : bool
Γ `+
: int → int → int
Γ ist jeweils beliebig, z.B. Γ = {} oder Γ = {x : int, b : bool}.
Γ ` 1 : int ist eigentlich ein Axiomenschema, denn für jedes Γ gibt
es ein Axiom.
Andreas Abel (LMU)
Typinferenz
06.06.11
10 / 27
Typisierung
Typisierung von Variablen
Der Typ einer Variablen ist im Kontext notiert:
(x : A) ∈ Γ
var
Γ `x :A
Äquivalent:
Γ ` x : Γ(x)
var
Beispiele:
{x : real}
{x : real, y : int, z : bool}
{x : real, y : int, z : bool}
{}
Andreas Abel (LMU)
`
`
`
`
x
y
y
x
Typinferenz
: real
: int
: real
: real
gültig
gültig
falscher Typ
ungebundene Variable
06.06.11
11 / 27
Typisierung
Typisierung der Anwendung
Der Typ der Anwendung einer Funktion e vom Typ A → B auf ein
Argument e 0 vom Typ A ist B.
Γ `e:A→B
Γ ` e0 : A
app
Γ ` e e0 : B
Falls e keinen Funktionstyp hat oder e 0 nicht im Definitionsbereich
(engl. domain) A liegt, ist die Anwendung nicht wohlgetypt.
Beispiel: Γ = {a : int, y : int}
Γ ` fn x ⇒ + a x : int → int
Γ ` * 2 y : int
app
Γ ` (fn x ⇒ + a x) (* 2 y) : int
Andreas Abel (LMU)
Typinferenz
06.06.11
12 / 27
Typisierung
Typisierung der Abstraktion
Hat e den Typ B unter der Annahme x : A, so hat fn x ⇒ e den Typ
A → B.
Γ, x : A ` e : B
abs
Γ ` fn x ⇒ e : A → B
Herleitungsbaum (Bsp.):
{x : α, f : α → β} ` f : α → β
var
{x : α, f : α → β} ` x : α
app
{x : α, f : α → β} ` f x : β
{x : α} ` fn f ⇒ f x : (α → β) → β
abs
{} ` fn x ⇒ fn f ⇒ f x : α → (α → β) → β
Andreas Abel (LMU)
Typinferenz
var
abs
06.06.11
13 / 27
Typisierung
Herleitung
Eine Herleitung ist eine Folge von (Typ)aussagen J1 , . . . , Jn , so dass
jedes Ji entweder ein Axiom ist, oder mit Konklusion einer Regel mit
Prämissen in J1 , . . . , Ji−1 .
Die hergeleitete Aussage ist die letzte, Jn .
Herleitung in linearer Form:
1
2
3
4
5
{x
{x
{x
{x
{}
: α, f : α → β} ` f : α → β
: α, f : α → β} ` x : α
: α, f : α → β} ` f x : β
: α} ` fn f ⇒ f x : (α → β) → β
` fn x ⇒ fn f ⇒ f x : α → (α → β) → β
var
var
app(1, 2)
abs(3)
abs(4)
Eine Aussage ist herleitbar, wenn sie eine Herleitung hat.
Andreas Abel (LMU)
Typinferenz
06.06.11
14 / 27
Typisierung
Kontexterweiterung
Eine Typaussage bleibt gültig, wenn wir den Typisierungkontext
erweitern. Bsp:
{x : α}
{x : α, y : γ}
{f : int → bool, x : α, y : γ}
` fn f ⇒ f x : (α → β) → β
` fn f ⇒ f x : (α → β) → β
` fn f ⇒ f x : (α → β) → β
Lemma (Kontexterweiterung/Abschwächung (engl. weakening))
Wenn Γ0 ⊇ Γ und Γ ` e : A herleitbar ist, dann auch Γ0 ` e : A.
Beweisbar durch Induktion über die Länge der Herleitung
J1 , . . . , Jn , Γ ` e : A.
Die folgende Regel ist also zulässig (engl. admissible):
Γ0 ⊇ Γ
Γ `e:A
weak
0
Γ `e:A
Andreas Abel (LMU)
Typinferenz
06.06.11
15 / 27
Polymorphie und Substitutionen
Polymorphie
Viele Ausdrücke haben mehrere Typisierungen. Z.B. f x
{x
{x
{x
{x
: int, f : int → int}
: bool, f : bool → int}
: α, f : α → α}
: α, f : α → β}
`
`
`
`
f
f
f
f
x
x
x
x
:
:
:
:
int
int
α
β
Die letzte dieser Typisierungen ist die allgemeinste (engl. principal
typing); jede andere Typisierung von f x erhält man daraus durch
Einsetzen von Typen für die Typvariablen α und β (Instanziierung).
{x : α, f : α → β}[bool/α, int/β] ` f x : β[bool/α, int/β]
Die allgemeinste Typisierung ist bis auf Typvariablen-Umbenennung
eindeutig.
Andreas Abel (LMU)
Typinferenz
06.06.11
16 / 27
Polymorphie und Substitutionen
Typsubstitutionen
Eine Typsubstitution σ ist eine endliche Abbildung von Typvariablen
α auf Typausdrücke A.
σ1 =
=
=
σ2 =
σ3 =
σ4 =
[bool/α, int/β]
[bool/α, int/β, γ/γ]
[bool/α, int/β, γ/γ, δ/δ] . . .
[β/α, (β → bool)/β, int/γ]
[γ/α, δ/β]
[β/α, α/β]
Für die Anwendung Aσ einer Substitution σ auf Typen A gilt z.B.:
int σ
= int
(A → B)σ
= Aσ → Bσ
α[bool/α, int/β] = bool
Andreas Abel (LMU)
Typinferenz
06.06.11
17 / 27
Polymorphie und Substitutionen
Instanziierung
Die Anwendung Γσ einer Substitution σ auf einen Typkontext Γ ist
punktweise definiert: (Γσ)(x) = (Γ(x))σ.
{x : int, y : α → β, z : α}[γ/α, int/β, bool/γ] =
{x : int, y : γ → int, z : γ}
Alle Typaxiome und -regeln bleiben unter Substitution gültig.
Lemma (Typerhaltung unter Substitution)
Wenn Γ ` e : A, dann Γσ ` e : Aσ.
Zulässige Regel:
Γ `e:A
subst
Γσ ` e : Aσ
Andreas Abel (LMU)
Typinferenz
06.06.11
18 / 27
Polymorphie und Substitutionen
Substitutionskomposition
Die Komposition σσ 0 ist definiert durch A(σσ 0 ) = (Aσ)σ 0 .
Berechnung der Komposition zweier Substitutionen:
α[(α → β)/α][int/α, bool/β] = (α → β)[int/α, bool/β]
= int → bool
β[(α → β)/α][int/α, bool/β] = β[int/α, bool/β]
= bool
[(α → β)/α][int/α, bool/β] = [(int → bool)/α, bool/β]
Komposition ist assoziativ, σ(σ 0 σ 00 ) = (σσ 0 )σ 00 , aber nicht
kommutativ.
Die Identitätssubstitution id = [] ist neutrales Element der
Komposition, es gilt id σ = σ und σ id = σ.
Andreas Abel (LMU)
Typinferenz
06.06.11
19 / 27
Polymorphie und Substitutionen
Prinzipale Typisierung
Eine Typisierung Γ ` e : A eines Ausdrucks e heißt prinzipal oder
allgemeinstmöglich, falls für jede weitere Typisierung Γ0 ` e : A0 von
e eine Substitution σ gibt, so dass A0 = Aσ und Γ0 ⊇ Γσ.
{x
{x
{x
{x
: α, f : α → β}
: int, f : int → int}
: α, f : α → β, y : γ}
: γ, f : γ → β}
`
`
`
`
f
f
f
f
x
x
x
x
:
:
:
:
β
prinzipal
int σ = [int/α, int/β]
β
σ = id
β
σ = [γ/α] (auch prinzipal)
Überladene Operatoren haben keine allgemeinste Typisierung.
+ : int → int → int
+ : real → real → real
Ohne Überladung hat jeder SML-Ausdruck eine prinzipale Typisierung.
Andreas Abel (LMU)
Typinferenz
06.06.11
20 / 27
Typinferenz
Typinferenz
Typinferenz berechnet zu jedem Ausdruck den allgemeinsten Typ
(Fehlermeldung, falls der Ausdruck keinen Typ hat).
Der prinzipale Typ einer Applikation e e 0 muss aus den prinzipalen
Typen von e und e 0 berechnet werden.
{} ` e : (int → β) → (γ → β)
{x : α} ` e 0 : α → real
{x : int} ` e e 0 : γ → real
Beide Prämissen müssen mit [int/α, real/β] instanziiert werden.
Allgemeinste Lösung der Gleichung int → β = α → real.
{f : α} ` f : α
{x : β} ` x : β
{f : β → γ, x : β} ` f x : γ
Löse Gleichung α = β → γ für neue Typvariable γ.
Andreas Abel (LMU)
Typinferenz
06.06.11
21 / 27
Typinferenz
Unifikation
Eine Substitution σ ist allgemeiner als σ 0 , falls es eine Substitution τ
gibt mit σ 0 = στ .
Die speziellere Substitution σ 0 ist also eine Instanz von σ.
Ein Unifikator zweier Typen A und A0 ist eine Substitution σ, so dass
Aσ = A0 σ.
(α → β)[int/α, int/β] = (int → β)[int/α, int/β]
Der allgemeinste Unifikator von A und A0 ist die allgemeinste
Substitution σ, so dass Aσ = A0 σ.
(α → β)[int/α] = (int → β)[int/α]
Andreas Abel (LMU)
Typinferenz
06.06.11
22 / 27
Typinferenz
Robinson’s Unifikationsalgorithmus
Der Unifikationsalgorithmus unify arbeitet auf einer Menge
E = {A1 = B1 , . . . , An = Bn } von formalen Typgleichungen und liefert
die allgemeinste Substitution σ, so dass A1 σ = B1 σ, . . . , An σ = Bn σ,
oder einen Typfehler.
1
unify({}) = id (* Fertig! *)
2
unify({A = A} ] E ) = unify(E ) (* redundante Gleichung *)
3
unify({A → A0 = B → B 0 } ] E ) = unify({A = B, A0 = B 0 } ] E )
4
unify({α = B} ] E ) = unify({B = α} ] E ) =
if α occurs in B then “Error: circularity”
else let σ = [B/α] in σ unify(E σ) (* Substitutionskomposition! *)
5
Sonst unify(E ) = “Error: type mismatch” (*z.B. (int = bool) ∈ E *)
Andreas Abel (LMU)
Typinferenz
06.06.11
23 / 27
Typinferenz
Beispiele Unifikation
Nur Variablen:
unify{α = β, β = γ} = [β/α]unify{β = γ} = [β/α][γ/β]unify{}
= [β/α][γ/β]id
= [β/α][γ/β]
= [γ/α, γ/β]
Typkonstruktorkonflikt:
unify{int → α = α → real} = unify{int = α, α = real}
= [int/α]unify{int = real} = ”Error: type mismatch”
Zirkulärer Typ:
unify{β = β, α = α → real} = unify{α = α → real}
= ”Error: circularity”
Andreas Abel (LMU)
Typinferenz
06.06.11
24 / 27
Typinferenz
Typinferenzalgorithmus
inferΓ (e) = (A, σ) nimmt einen Ausdruck e in Typkontext Γ und liefert den
allgemeinsten Typ A von e samt der allgemeinsten Instanziierung σ von Γ,
so dass Γσ ` e : A.
1
inferΓ (x) = if (x : A) ∈ Γ then (A, id)
else “Error: unbound variable”
2
inferΓ (c) = let A = type of constant c
in (A, id)
3
inferΓ (fn x ⇒ e) =
let α new (previously unused) type variable
let (B, σ) = inferΓ,x:α (e) (* hence (Γσ, x : ασ) ` e : B *)
in (ασ → B, σ) (* thus, Γσ ` fn x ⇒ e : ασ → B *)
Andreas Abel (LMU)
Typinferenz
06.06.11
25 / 27
Typinferenz
Typinferenzalgorithmus (Applikation)
inferΓ (e) = (A, σ) nimmt einen Ausdruck e in Typkontext Γ und liefert den
allgemeinsten Typ A von e samt der allgemeinsten Instanziierung σ von Γ,
so dass Γσ ` e : A.
4
inferΓ (e e 0 ) =
let (C , σ) = inferΓ (e) (* Γσ ` e : C *) (* Γσσ 0 ` e : C σ 0 *)
let (A, σ 0 ) = inferΓσ (e 0 ) (* Γσσ 0 ` e 0 : A *)
let σ 00 = unify{C σ 0 = A → β} for new β (* C σ 0 σ 00 = Aσ 00 → βσ 00 *)
in (βσ 00 , σσ 0 σ 00 ) (* Γσσ 0 σ 00 ` e e 0 : βσ 00 *)
Dieser Algorithmus heißt W und geht auf Roger Hindley und Robin Milner
zurück.
Andreas Abel (LMU)
Typinferenz
06.06.11
26 / 27
Zusammenfassung
Zusammenfassung
Well-typed programs do not go wrong (Robin Milner)
Typisierung Γ ` e : A.
Typaxiome, Typregeln, Typherleitung.
Polymorphie und allgemeinste (prinzipale) Typen.
SML-Typinferenz berechnet zu jedem Ausdruck prinzipalen Typ.
Typinferenz beruht auf Unifikation (Robinson).
Andreas Abel (LMU)
Typinferenz
06.06.11
27 / 27
Herunterladen