1 Vollständige partielle Ordnungen, stetige Funk

Werbung
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
1
1
Vollständige partielle Ordnungen, stetige Funktionen
Die Betrachtung vollständiger partieller Ordnungen soll eine einfache und
verständliche Einführung in einen Ansatz zur denotationellen Semantik liefern. Die Sprache, die wir dazu betrachten werden, ist PCF, eine Variante
einer funktionalen Programmiersprache.
Polymorph getypte funktionale Programmiersprachen und deren denotationelle Semantik würden den Rahmen dieser Vorlesung sprengen.
Definition 1.1 Eine partielle Ordnung ≤ auf einer Menge P ist definiert
durch die Eigenschaften:
• reflexiv: ∀x ∈ P : x ≤ x
• transitiv: ∀x, y, z ∈ P : x ≤ y, y ≤ z =⇒ x ≤ z
• antisymmetrisch ∀x, y, ∈ P : x ≤ y ∧ y ≤ x =⇒ x = y
Definition 1.2 Sei X ⊆ P .
• Sei p ∈ P . Wenn ∀x ∈ X : x ≤ p dann ist p obere Schranke von X.
• p ∈ P ist eine kleinste obere Schranke (obere Grenze, lub, least upper
bound) von X, wenn p obere Schranke von X undFfür jede andere obere
Schranke q von X gilt p ≤ q. Bezeichnung: p = X.
• Analog kann man untere Schranken, glb, untere Grenze usw. definieren
Definition 1.3 Eine Folge a1 ≤ a2 ≤ a3 ≤ . . . mit Elementen ai ∈ P heißt
Kette (ω-Kette).
• Die partielle Ordnung ≤ auf P ist vollständig, wenn jede aufsteigende
Kette a1 ≤ aF
2 ≤ a3 ≤ . . . eine kleinste obere Schranke in P hat.
Bezeichnung: i ai .
• Wenn die partielle Ordnung P ein kleinstes Element ⊥ hat, dann sagt
man, ≤ ist eine Ordnung mit kleinstem Element.
• Als Abkürzung sprechen wir bei einer vollständig partiellen Ordnung
mit kleinstem Element von einer cpo mit ⊥. Wenn die Bezeichnung ⊥
nicht eindeutig ist, indizieren wir das ⊥ mit der zugehörigen cpo.
Wir beschäftigen uns im folgenden mit vollständigen partiellen Ordnungen (cpo’s).
Beispiel 1.4
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
2
• Die Potenzmenge einer Menge mit ⊆ ist eine cpo mit kleinstem Element.
Für
F
S eine aufsteigende Kette M1 ⊆ M2 ⊆ M3 ⊆ . . . gilt
i Mi .
i Mi =
• Die Menge der partiellen Funktionen: M → M mit der Teilmengenbeziehung ist eine cpo mit kleinstem Element.
• Die zweielementige Menge {⊥, >} mit ⊥ ≤ > ist eine cpo.
Eine diskrete (flache) cpo ist eine partielle Ordnung, in der nur x ≤ x
für alle Elemente x gilt.
Definition 1.5
Monotonie Eine Funktion f : D → E, wobei D, E cpo’s sind, ist monoton,
gdw. für alle d1 ≤D d2 gilt, dass auch f (d1 ) ≤E f (d2 ).
Stetigkeit Die Funktion f : D → E ist stetig, wenn sie monotonFist und
wenn für alle Ketten d1 ≤D d2 ≤D d3 ≤D . . . gilt, dass f ( i di ) =
F
i f (di ).
Bemerkung 1.6 Die Intuition dahinter ist:
stetig = algorithmisch sinnvoll
Bei Anwendung auf eine Programmiersprache wird sich ergeben, dass
alle berechenbaren Funktionen eine stetige Denotation haben.
In manchen Semantiken kommt es vor, dass es stetige Funktionen gibt,
die nicht Denotation einer Funktion sind.
Im folgenden bezeichnen wir für eine Funktion f : D → D. die mehrfache
Anwendung f (. . . f (d) auch als f n (d).
| {z }
n
Beispiel 1.7 Alle Funktionen, die auf einer diskreten cpo definiert sind,
sind stetig.
Eine wichtige Beobachtung, die in ähnlicher Form oft verwendet wird
zur Konstruktion kleinster Fixpunkte, ist folgendes:
In einer cpo mit ⊥ sei eine monotone Funktion f gegeben. Dann ist f i (⊥)
eine Kette. Es gilt ⊥ ≤ f (⊥) ≤ f (f (⊥)) ≤ . . . ≤ f n (⊥) ≤ f n+1 (⊥) ≤ . . ..
Aussage 1.8
• Die Identität auf einer cpo ist stetig
• Die Komposition von stetigen Funktionen ist stetig. D.h. f ◦g ist stetig,
wenn f, g stetig sind.
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
3
Beweis. Die Identität ist monoton. Da eine Kette unter Id auf sich selbst
abgebildet wird, gilt das auch für den lub der Kette(n).
Der Rest ist eine Übungsaufgabe.
2
Definition 1.9 Sei f : D → D eine Funktion, wobei D eine cpo ist. Dann
ist d ∈ D ein
• Fixpunkt von f , wenn f (d) = d ist.
• Präfix-punkt von f , falls f (d) ≤ d
• Postfix-punkt von f , falls d ≤ f (d).
Satz 1.10 (Fixpunktsatz)
Sei f : D → D eine stetige Funktion, wobei D eine cpo mit kleinstem
Element ⊥ ist. Dann hat f F
einen Fixpunkt, insbesondere einen kleinsten
Fixpunkt a. Zudem gilt a = f n (⊥). a ist auch der kleinste Präfix-punkt
von f . Diesen kleinsten Fixpunkt nennen wir f ix(f ).
F
Beweis.FDa D eine cpo ist, existiert f n (⊥)). Es gilt:
f ( F f n (⊥))
= f n+1 (⊥)) (Stetigkeit von f und da
⊥ ≤ f (⊥) ≤ ...f n (⊥) . . .
aufsteigende Kette ist)
F
= f n (⊥))
da f (⊥) ≥ ⊥ und
F nes bis auf ⊥ die gleiche Kette ist.
Damit haben wir gezeigt, dass f (⊥)) ein Fixpunkt von f ist. Jetzt
zeigen wir, dass dies auch der kleinste ist.
Sei d irgendein Fixpunkt von f . D.h. f (d) = d.
Dann gilt: ⊥ ≤ d, also auch f (⊥) ≤ d = f (d), da f Fmonoton ist. Mit
Induktion
nach n erhält man: f n (⊥) ≤ d, also auch f n (⊥)) ≤ d, da
F n
f (⊥)) kleinste obere Schranke der aufsteigenden Kette ⊥ ≤ f (⊥) ≤
...f n (⊥) . . . ist.
F
Dieselbe Argumentation zeigt auch dass f n (⊥)) kleinster Präfixpunkt
von f ist.
2
Es gilt folgender allgemeinerer Satz (siehe z.B. Davey und Priestley)
Satz 1.11 Sei f : D → D eine monotone Funktion, wobei D eine cpo ist.
Dann hat f einen kleinsten Fixpunkt.
Allerdings
ist der kleinste Fixpunkt in diesem allgemeinen Fall i.a. nicht als
F n
f (⊥) konstruierbar, auch wenn es ein kleinstes Element ⊥ gibt.
Beispiel 1.12 Für eine nicht-monotone Funktion.
Gegeben sei die Boolesche cpo: {True, False, ⊥} mit ⊥ ≤ True, ⊥ ≤ False.
Die Funktion, die Terminierung testet ist:
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
4
f : ⊥ → False, True → True, False → True.
Diese Funktion f ist nicht monoton. Denn: ⊥ ≤ True, aber f ⊥ = False 6≤
True = f True.
Beispiel 1.13 Für eine monotone, aber nicht stetige Funktion.
Betrachte unendliche Strings (oder alternativ unendliche Listen).
Wir wollen Eingabeströme modellieren, wobei diese endlich oder unendlich sein können, und nur aus 0, 1, bestehen.
Modellierung: Als Menge benutzen wir (endliche und unendliche) Strings
bestehend aus 0, 1 und $, wobei $ nur am Ende sein darf. Endliche Strings
ohne $ am Ende betrachtet man als unvollständige Strings (bzw. Eingabe).
Formal könnte man schreiben S := ({0, 1}∗ + {0, 1}∗ $) ∪ { alle unendlichen Folgen bestehend aus 0,1 }.
ε bezeichne den leeren String. Als Ordnung verwenden wir s ≤ t, wenn
s Präfix von t.
Maximale Strings in S sind die unendlichen und mit $ beendete Strings.
Die Menge S ist eine cpo mit kleinstem Element ε: Vollständigkeit: interessant sind nur die unendlichen (echten) Folgen. Deren lubs sind unendliche
Strings.
Wir betrachten eine Funktion auf S, die durch Durchlesen des Strings
von links nach rechts feststellt, ob eine 1 in einem String ist oder nicht:
hateins : S → {True, False}
Das Problem ist jetzt, dass diese Funktion zwar leicht ja sagen kann,
aber nein nur, wenn sie bis zum $ keine 1 gefunden hat. Sind bis zu einem
Zeitpunkt nur 0’en aufgetaucht, so gibt es noch keine Antwort. hateins(ε)
bedeutet, dass im Moment auf weitere Eingabe gewartet werden muß.
Im schlechtesten Fall gibt diese Funktion keine Antwort: wenn der unendlich lange String aus 0’en die Eingabe ist. Entweder ist dann unsere
Funktion partiell oder wir brauchen einen Wert “unbekannt“. Da wir bereits
festgestellt haben, dass partielle Funktionen auch als totale Funktionen auf
einem mit ⊥ erweiterten Bereich modelliert werden können, nehmen wir die
zweite Alternative:
{True, False, ⊥} mit ⊥ ≤ True und ⊥ ≤ False.
Die Anforderungen an hateins sind:
hateins(1s) = True
hateins($) = False
hateins(0s) = hateins(s)
hateins(ε) = ⊥
Diese bestimmen den Wert für den unendlichen String aus Nullen
000 . . . = 0ω nicht eindeutig.
Sinnvoll wären False oder ⊥. Der logisch richtige Wert wäre False,
aber dieser läßt sich algorithmisch erst nach unendlich vielen Überprüfungen
bestimmen.
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
5
Betrachtet man das Verhalten der Funktionen bzgl. der cpo, dann korrespondiert das “richtige“ Berechnungsverhalten zur Stetigkeit der Funktion
hateins.
00 . . . läßt sich approximieren durch die aufsteigende Folge: ε, 0, 00, 000, . . ..
Der lub davon ist gerade 0ω . Der jeweilige Wert von
hateins(ε), hateins(0), . . . . . . ist ⊥. Stetigkeit bedeutet dann, dass
hateins(0ω ) = ⊥.
Die Funktion hateins0 mit hateins0 (0ω ) = False wäre nicht mehr stetig, da False nicht der lub der approximierenden Folge ist.
1.0.1
Vollständige Verbände, Knaster-Tarski Fixpunktsatz
Wir erwähnen noch den Fixpunktsatz für vollständige Verbände.
Definition 1.14
• Eine Ordnung ≤ auf D ist ein Verband, gdw, für beliebige Elemente
x, y ∈ D sowohl glb(x, y) als auch lub(x, y) in D existieren.
• Eine Ordnung ≤ auf D ist ein vollständiger Verband gdw. für beliebige
Teilmengen A ⊆ D sowohl glb(A) als auch lub(A) existiert (eindeutig).
Ein vollständiger Verband ist insbesondere eine cpo mit ⊥, denn jeder
vollständige Verband hat ein kleinstes und ein größtes Element.
Die Potenzmenge P(M ) einer Menge
vollständiger VerS M ist immer einT
band bzgl ⊆. lub und glb sind durch {Mi } bzw. durch {Mi } definiert.
In einem Verband genügt die Monotonie einer Funktion zur Existenz
eines Fixpunktes. Der ist auch einfach zu konstruieren.
Satz 1.15 (Knaster, Tarski) Sei D ein Verband und Φ : D → D eine
monotone Abbildung. Dann existiert
ein kleinster und größter Fixpunkt von
V
Φ: der kleinste Fixpunkt ist {x | Φ(x) ≤ x}.
V
Beweis. Sei H := {x ∈ D | Φ(x) ≤ x} und sei α = H. Für alle x ∈ H
gilt α ≤ x, also gilt Φ(α) ≤ Φ(x) ≤ x (Monotonie). Deshalb ist Φ(α) untere
Schranke von H; somit Φ(α) ≤ α.
Aus Φ(α) ≤ α folgt jetzt Φ(Φ(α)) ≤ Φ(α) und damit nach Definition von H auch Φ(α) ∈ H D.h. aber, α ≤ Φ(α) und somit folgt aus der
Antisymmetrie: α = Φ(α). D.h. α ist ein Fixpunkt.
Da alle Fixpunkte β per Definition in H sind, muß α kleinster Fixpunkt
von Φ sein.
2
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
1.1
6
Konstruktion neuer cpo’s
Wir konstruieren neue vollständige partielle Ordnungen, um aus einfachen
semantischen Bereichen, wie z.B. dem der natürlichen Zahlen und der Booleschen Werte neue cpos, d.h. neue semantische Bereiche zu konstruieren.
Wir werden sehen, dass den Konstruktionsverfahren jeweils bestimmte Programmkonstrukte entsprechen.
Zunächst benötigen wir den Begriff der Isomorphie zweier cpo’s:
Definition 1.16 Seien D, E zwei cpo’s. Eine stetige Funktion f : D → E
ist ein Isomorphismus, wenn sie eine Bijektion ist und wenn es eine stetige,
bijektive Funktion g : E → D gibt, so dass f ◦ g = IdE und g ◦ f = IdD ist.
D und E heißen dann isomorph.
I. a. unterscheiden wir isomorphe cpo’ s nicht, da sie im wesentlichen dasselbe
Objekt sind.
Aussage 1.17 Seien D, E zwei cpo’s. Eine Funktion f : D → E ist ein
Isomorphismus, gdw. f eine Bijektion ist mit x ≤D y gdw. f (x) ≤E f (y).
Beweis. Die eine Richtung ist offensichtlich.
Sei f : D → E eine Bijektion mit x ≤D y gdw. f (x) ≤E f (y). Sei g die
inverse Funktion zu f . Aus der Voraussetzung folgt, dass g ebenfalls die
Monotonie-Eigenschaft von f hat. Sei a1 ≤D a2 ≤D . . . eine aufsteigende
Kette und sei a die kleinste obere Schranke zu ai . Dann existiert zu f (a1 ) ≤E
f (a2 ) ≤E . . . eine kleinste obere Schranke e. Da f (ai ) ≤E f (a) für alle i,
gilt e ≤ f (a). Wegen der Voraussetzung ist ai ≤ g(e) für alle i, d.h. g(e) ist
obere Schranke der ai mit g(e) ≤ a. Da a obere Grenze ist, gilt g(e) = a und
damit auch e = f (a). D.h. f ist stetig. Die Funktion g ist ebenfalls stetig,
da g die Voraussetzung der Proposition erfüllt.
2
1.2
Diskrete cpo’s
In diskreten cpo’s gilt nur x ≤ x für alle x ∈ D. Jede Kette ist konstant: x ≤
x ≤ . . .. Diese cpo’s kommen vor bei Booleschen Wahrheitswerten, oder der
Menge der Zahlen. Die Zahlen sind bzgl. Informationsgehalt unvergleichbar,
da sie jeweils den maximalen Informationsgehalt repräsentieren.
Es gilt: Jede Funktion f : D → E ist stetig, wenn D eine diskrete cpo
ist.
1.3
Endliche Produkte.
Seien Di , i = 1, . . . , k cpo’s. Wir können immer annehmen, dass die Mengen
disjunkt sind. D1 × . . . × Dk ist das zugehörige cartesische Produkt (die
Menge der k-Tupel). Die Ordnung darauf ist punktweise definiert:
7
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
(d1 , . . . , dk ) ≤ (d01 , . . . , d0k ) gdw. di ≤ d0i für alle i.
F
Es gilt für Ketten von Tupeln (d1j , . . . , dkj ), dass j (d1j , . . . , dkj ) =
F
F
( j d1j , . . . , j dkj ). (Übungsaufgabe)
Die Projektionsfunktionen πi sind definiert durch πi (d1 , . . . , dk ) = di .
Diese Projektionsfunktionen sind stetig.
Tupelbildung kann man für Funktionen ebenfalls verwenden:
Seien fi : E → Di , i = 1, . . . , k stetige Funktionen auf den cpo’s E, Di .
Dann ist die Funktion hf1 , . . . , fk i : E → (D1 , . . . , Dk ) definiert durch
hf1 , . . . , fk i e := (f1 (e), . . . , fk (e)). Die Funktion hf1 , . . . , fk i erfüllt πi ◦
hf1 , . . . , fk i = fi . Die Funktion hf1 , . . . , fk i ist offenbar monoton.
Sie ist auch stetig:
Sei e0 ≤ e1 ≤ e2 ≤ . . . eine Kette in E.
=
=
=
=
F
hf1 , F
. . . , fk i( i ei )F
(fF1 ( i ei ), . . . , fF
k ( i ei ))
(F i (f1 (ei ), . . . , i fk (ei )) da alle fi stetig sind
(Definition des Produktes)
Fi ((f1 (ei ), . . . , fk (ei ))
hf
,
.
.
.
,
f
i(e
)
Definition
von h. . .i
i
1
k
i
Die Produktbildung kann man für Funktionen und Kreuzprodukt von
cpo’s gemeinsam machen:
Seien fi : Di → Ei stetige Funktionen auf cpo’s. Dann ist
f1 × . . . × fk : D1 × . . . × Dk → E1 × . . . × Ek definiert durch f1 × . . . ×
fk (d1 , . . . , dk ) = (f1 (d1 ), . . . , fk (dk )).
Dies kann man schreiben als f1 × . . . × fk = hf1 ◦ π1 , . . . , fk ◦ πk i.
Daraus kann man schließen, dass diese Funktion ebenfalls stetig ist.
Lemma 1.18 Sei h : E → D1 × . . . × Dk eine Funktion auf cpo’s. Dann ist
h stetig gdw. πi ◦ h stetig ist für alle i.
Beweis. “ =⇒ “: Komposition ist stetig.
“⇐“: Nehme an, dass πi ◦ h stetig ist für alle i. Dann kann man h aus
stetigen Operationen rekonstruieren: h(x) = (π1 ◦ h(x), . . . , πk ◦ h(x)) =
h(π1 ◦ h, . . . , πk ◦ hi(x). Diese Funktion ist stetig, da πi ◦ h stetig ist, und
h. . .i stetige Funktionen erzeugt.
2
Wir zeigen auch eine analoge Aussage für eine Funktion h : D1 × . . . ×
Dk → E. Diese Aussage ist etwas schwerer zu zeigen.
Dazu benötigen wir ein Lemma über lubs von aufsteigenden arrays.
Lemma 1.19 Sei ei,j Elemente einer cpo E, so dass ei,j ≤ ei0 ,j 0 wenn i ≤ i0
und j ≤ j 0 . Dann gilt: {ei,j | i, j ∈ IN} hat eine kleinste obere Schranke und
G
GG
GG
G
ei,j = ( ei,j ) = ( ei,j ) =
ei,i
i,j
i
j
j
i
i
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
8
Beweis. Es F
ist eine Fleichte Übung, zu zeigen, dass die Mengen
{ei,j }, {ei,i }, { j ei,j }, { i ei,j } die gleichen oberen Schranken haben.
Zunächst
F gilt, dass ei,j ≤ ek,k wenn k = max{i, j}. Sei a eine obere Schranke
von { j ei,j }. Dann ist a auch obere Schranke von
F {ei,j }. Umgekehrt sei a
obere Schranke von {ei,j }. Dann ist für jedes i: j ei,j ≤ a, denn das linke
ist eine kleinsteFobere Schranke einer Teilmenge. Damit ist a auch obere
Schranke von { j ei,j }.
2
Lemma 1.20 Sei f : D1 × . . . × Dk → E eine Funktion auf cpo’s Di , E.
Dann ist f stetig gdw. f stetig in jedem Argument ist.
Beweis. “ =⇒ :“ Sei f stetig: Für feste dj definiere die Funktion fi (x) :=
f (d1 , . . . , di−1 , x, di+1 , . . . , dk ). Diese ist stetig.
“⇐:“ Sei der Einfachheit halber k = 2 und sei (x0 , y0 ) ≤ (x1 , y1 ) ≤ . . . eine
aufsteigende Kette. Dann gilt:
F
f (Fi (xi ,F
yi ))
= f ( i xi , j yj ) lubs für kartesisches Produkt
F
F
=
f (xi , j yj ) da f stetig im ersten Argument
i
F F
( f (xi , yj ) da f stetig im zweiten Argument
=
Fi j
wegen obigem Lemma
=
i f (xi , yi )
2
1.4
Funktionenräume
Seien D, E cpo’s.
Mit [D → E] bezeichnen wir die Menge der stetigen Funktionen D → E.
Die Ordnung auf [D → E] sei f ≤ g gdw. ∀d ∈ D : f (d) ≤ g(d).
Wenn E ein bottom-Element hat, dann hat [D → E] das kleinste Element ⊥[D→E] , definiert durch: ⊥[D→E] d = ⊥E für alle d ∈ D.
Die Ordnung [D → E] ist eine cpo:
Betrachte eine Kette f0 ≤ f1 ≤ f2F≤ . . . Für d ∈ D können wir
F f0 (d) ≤
f1 (d) ≤ . . . bilden.
existiert i fi (d). D.h die Funktion i fi ist so
F Deshalb F
definiert, dass ( i fi )(d) := F
i fi (d).
Es bleibt zu zeigen, dass ( i fi ) stetig ist, d.h. ∈ [D → E] ist.
Sei d0 ≤ d1 ≤ d2 ≤ . . . eine Kette in D. Dann gilt:
F
F
( i fi )( j dj )
F
F
F
= ( i fi ( j dj )
nach Definition der Funktion( i fi )
F F
=
f (d )
da fi stetig ist
Fi Fj i j
=
f
(d
)
nach dem Lemma über aufsteigende arrays
i i j
Fj F
=
j (( i fi )(dj )) Definition von lub für Funktionen
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
9
Beispiel 1.21 Wenn I eine Menge mit diskreter Ordnung ist, entspricht
der Funktionenraum [I → D] einem Produkt von zu D isomorphen Ordnungen, wobei I die Menge der Indizes ist. Wenn I endlich ist sind das gerade
|I|-Tupel.
1.5
Operationen auf Funktionenräumen
Anwendung einer Funktion auf das Argument:
apply : [D → E] × D → E wobei D, E cpo’s sind. Die Definition ist:
apply (f, d) = f (d).
Die Funktion apply ist stetig: Sie ist stetig in jedem Argument:
1. stetig im ersten Argument:
Sei f0 ≤Ff1 ≤ f2 ≤ eine
F Kette von Funktionen. Dann gilt:
apply((
i fi ), d) = ( i fi )d
F
(Definition des lubs von Funktionen)
= Fi fi (d)
= i apply(fi , d)
2. Stetig im zweiten Argument:
Sei d0 ≤ dF
1 ≤ d2 ≤ . . . eine Kette.
apply(f,
i ))
F ( i dF
= fF( i di ) = i f (di ) da f stetig
= i apply(f, di )
Beispiel 1.22 Currying macht aus einer Funktionen auf Tupeln eine Funktion, die man nacheinander auf die Elemente des Tupels anwenden kann.
(Das ist eine Konstruktion, die zeigt, dass man mit einstelligen Funktionen
im Prinzip auskommt)
curry :
[F × D → E] → (F → [D → E])
curry(g) : F → [D → E]
curry(g) = λv ∈ F.(λd ∈ D.g(v, d))
Wir argumentieren dass folgendes gilt:
1. curry ist korrekt definiert, d.h. das Bild liegt in (F → [D → E])
2. curry (g) ist stetig.
3. curryselbst ist stetig
Jetzt machen wir das mal von Hand. Man kann zeigen, dass alle diese
Konstruktionen automatisch stetig sind.
1. curry(g) ist korrekt definiert:
(λd ∈ D.g(v, d)) ist eine stetige Funktion [D → E].
Die Behauptung gilt, denn g ist stetig in jedem Argument.
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
10
2. curry(g) ist stetig für alle g: v0 ≤ v1 ≤ F
v2 ≤ . . . sei Kette in F . Sei
d ∈ DFbeliebig. Dann
betrachte
curry(g)(
i vi )d
F
= g((
F i vi ), d) = i g(vi , d) =
= i curry(g) vi d
F
F
Also stimmt die Funktion curry(g)( i vi ) mit ( i curry(g) vi ) überein. D.h. curry(g) ist stetig.
3. curryist stetig:
F Sei g1 ≤ g2 ≤ g3 ≤
F...
Dann
F gilt: i (curry(gi )) d e = i gi (d, e)
= Fi gi (d, e)
= ( i gi )(d,
F e) da gi ∈ [F × D → E]
= curry(
i gi ) d e
F
F
D.h. i curry(gi ) und curry( i gi ) stimmen als Funktionen überein.
Damit ist curry stetig.
1.6
Lifting
Diese Konstruktion addiert ein neues Bottom-Element zu einer cpo. Der
Zweck ist i.a., partiell definierte Funktionen handhabbarer zu machen, insbesondere diese auf bekannte Funktionen zurückzuführen. Man kann es auch
sehen als Umwandlung einer mathematisch gegebenen Berechnungsmöglichkeit in eine algorithmische, wobei “undefiniert“ und Nicht-terminierung mitbehandelt werden sollen.
Die Konstruktion selbst ist einfach: Sei D eine cpo. Definiere D⊥ =
D ∪ {⊥}, wobei ⊥ von allen Elementen von D verschieden ist. Die Ordnung
auf D⊥ ist die Ordnung von D, zusätzlich gilt ⊥ ≤ d für alle d ∈ D⊥ . Die
Einbettung ι : D → D⊥ ist die Funktion mit ι(d) = d.
Lemma 1.23 Die Einbettung ist stetig.
Sei E eine cpo mit kleinstem Element. Eine Funktion f : D → E kann
auf f : D⊥ → E fortgesetzt werden. Diese Funktion nennen wir f ∗ .
f (d) wenn d 6= ⊥
f ∗ (d) =
⊥E sonst. d.h. d = ⊥
f ∗ ist stetig: Sei d1 ≤ d2 : wenn d2 = ⊥, dann ist d1 = ⊥. wenn d2 6= ⊥,
dann ist f ∗ (d1 ) = f (d1 ) oder f ∗ (d1 ) = ⊥E ≤ f (d2 ).
Sei d1 ≤ d2 ≤ . . . eine aufsteigende Kette. Wenn ein di 6= ⊥, dann ist
lub f ∗ (di ) = lub f (di ) = f (lub di ) = f ∗ (lub di ) da (lub di ) 6= ⊥. Wenn
di = ⊥, dann ist lub f ∗ (di ) = lub ⊥ = ⊥ = f ∗ (lub di ).
Bemerkung 1.24 Wenn f durch eine Funktion λx.e beschrieben werden
kann, dann schreiben wir statt (λx.e)∗ (d) auch (slet x = d in e) (slet soll
“striktes let“ andeuten)
! Das “normale“ (let x = d in e) kann als (d e) übersetzt werden.
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
11
Lemma 1.25 Die (Lifting-)Funktion (.)∗ : [D → E] → [D⊥ → E] ist stetig:
Beweis. Sei f ≤ g. Sei d ∈ D⊥ . Wenn d = ⊥, dann ist f ∗ ⊥ = ⊥ = g ∗ ⊥.
Wenn d 6= ⊥, dann betrachte f d ≤ g d. Wegen f ∗ (d) = f (d) und g ∗ (d) =
g(d) ist (.)∗ monoton.
Sei f0 ≤ f1 ≤ . . . eine
in [D → E] undF d ein Element aus D⊥ .
F Kette
∗
Wenn d = ⊥, dann ist ( i fi ) ⊥ = ⊥. Die Kette i fi∗ ⊥ ist konstant = ⊥,
also ebenfalls = ⊥.
Sei d 6= ⊥. Dann gilt:
F
F
(F i fi )∗ d = F i fi )d (denn so war (.)∗ definiert)
= Fi fi (d) = i fi∗ (d) Stetigkeit
von fi und Definition von (.)∗
F
= ( i fi∗ )(d)
Da ( i fi∗ ) punktweise definiert ist
2
Mathematische Operationen auf Mengen können damit “geliftet werden“. Zum Beispiel kann man damit Multiplikation oder andere Funktionen
auf Zahlen auch für Argumente verwenden, die möglicherweise undefiniert
sind. Die Zahlen werden dann als geliftete Zahlen betrachtet, d.h. als ein
flache cpo mit einem kleinstem Element. Diese Methode geht dann davon
aus, dass diese neuen Operationen jeweils “undefiniert“ liefern für undefinierte Argumente. D.h. man kann sie gemeinsam mit anderen Funktionen
verwenden, die rekursiv definiert und möglicherweise für bestimmte Werte
undefiniert sind.
geliftete Multiplikation:
∗⊥ := λa, b.(slet x = a, y = b in x ∗ y)
Diese Definition liefert auch für das Produkt 0 ∗⊥ ⊥ = ⊥.
geliftete Disjunktion (oder)
∨⊥ := λa, b.(slet x = a, y = b in x ∨ y)
Man sieht: dieses “oder“ muß erst testen, ob die Argumente definiert
sind. Z.B. False ∨⊥ ⊥ = ⊥. D.h. es hat eine andere Semantik als das in
Haskell definierte oder (||), das nur das erste Argument immer testet.
Für die Untersuchung der Äquivalenzen im ungetypten (lazy) LambdaKalkül wird auch ein Lifting verwendet, das einen Funktionenraum (der
bereits ein kleinstes Element hat) nochmals liftet und mit einem extra kleinsten Element versieht. Damit ist es möglich, die Funktionen λx.⊥ und das
Element ⊥ zu unterscheiden.
Definition 1.26 Eine Funktion f : D → E , wobei D, E cpo’s mit ⊥ sind,
ist strikt gdw f ⊥ = ⊥
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
12
Dies ist eine Definition bzgl. Funktionen auf cpo’s. Bei der Semantik
von funktionalen Programmiersprachen hat dieser Begriff eine operationale
Bedeutung: wenn [[f ]] strikt ist, kann man die Optimierung verwenden, so
dass f zuerst sein Argument auswertet, und dann weiter reduziert.
Seien D, E cpos mit ⊥. Betrachte die Funktion strict : [D → E] →
[D → E], definert durch:
f (d) wenn d 6= ⊥
(strict f ) d =
⊥E wenn d = ⊥
Operational bedeutet dies, dass eine Funktion f verändert wird, indem
zunächst das Argument ausgewertet wird.
Es gilt:
1. (strict f ) ist stetig, wenn f stetig ist.
2. strict ist stetig: Der Beweis geht analog zu (*), zusätzlich sind ein
paar Fallunterscheidungen zu machen.
1.7
Summen von cpo’s; Fallunterscheidungen
Die Summen-Konstruktion ist die Grundlage der Semantik des caseKonstruktes.
Definition 1.27 Seien D1 , . . . , Dk (disjunkte)
S cpo’s. Die Summe dieser
cpo’s ist einfach die (disjunkte) Vereinigung Di , wobei die Ordnung genau
übertragen wird. Die Elemente aus verschiedenen cpo’s sind unvergleichbar.
Die Summe schreiben wir auch als D1 + . . . + Dk . Es gibt (injektive) Einbettungen ιi : Di → D1 + . . . + Dk mit ιi (di ) = di
Es gilt: Alle Einbettungen sind stetig. (offensichtlich)
Oft wird diese Summe explizit mit Konstruktoren konstruiert, da die
Di ’s möglicherweise nicht disjunkt sind: Wenn man ci als verschiedene Konstruktoren hat, dann definiert man D1 + . . . + Dk = {ci (di ) | i = 1, . . . , k}.
Man kann stetige Funktionen in eine gemeinsame cpo E auch “summieren“:
Sei fi : Di → E stetige Funktionen auf cpo’s.
Dann definiert man [f1 , . . . , fk ] : D1 + . . . + Dk → E folgendermaßen:
[f1 , . . . , fk ]ιi (di ) = fi (di ). D.h. [f1 , . . . , fk ] ◦ ιi = fi .
Hat man die Summe mit Konstruktoren ci definiert, dann definiert man:
[f1 , . . . , fk ]ci (di ) = fi (di ).
Es gilt: Die Operation [., . . . , .] : ([D1 → E] × . . . × [Dk → E]) → ((D1 +
. . . + Dk ) → E) ist stetig.
Die Summe von cpo’s kann zur Beschreibung des case-Konstruktes dienen. Das einfachste ist “ if“: Die diskrete cpo T = {True, False} kann man
als Summe der cpo’s True und False ansehen.
13
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Seien zwei Funktionen gegeben:
λx1 .e1 : True → E
λx2 .e2 : False → E
Dann kann man definieren:
cond0 (t, e1 , e2 ) := [λx1 .e1 , λx2 .e2 ](t)
Wenn t = True ergibt, dann erhält man (λx1 .e1 )(True), im anderen Fall
(λx2 .e2 )(False).
Da man i.a. die Situation hat, dass die Bedingung undefiniert sein kann,
betrachtet man E mit ⊥, liftet {True, False} zu {True, False}⊥ , und definiert
cond(t, e1 , e2 ) := (slet t = b in cond0 (t, e1 , e2 ))
Dies
ist
das
cond
mit
folgendem
Verhalten:
wenn t = ⊥,
dann ist cond(t, e1 , e2 ) = ⊥,
wenn t = True,
dann e1 (True)
wenn t = False, dann e2 (False)
D.h., Die Summenkonstruktion gehört zu einem if. Das kann man ohne
Probleme zu einem case verallgemeinern:
D1 + . . . + Dk sei Summe von cpo’s, die mit Konstruktoren ci konstruiert
worden ist. Wenn Funktionen λxi .ei : Di → E gegeben sind, dann entspricht
[λx1 .e1 , . . . , λxn .en ](d) einer Konstruktion mit case:
case d of (c1 (x1 ) → e1 (x1 ));
...
(cn (xn ) → en (xn ))
Im allgemeinen ist das zu schwach, da dies voraussetzt, dass die Auswertung,
die den Konstruktor erkennt, auch terminiert.
Deshalb nimmt man i.a. die geliftete Summe (D1 +. . .+Dk )⊥ als Summe
von cpo’s, die mit Konstruktoren ci konstruiert worden ist.
Wenn Funktionen λxi .ei : Di → E gegeben sind, dann entsprechen sich:
• (slet x = d in [λx1 .e1 , . . . , λxn .en ](d))
und
•
case d of
1.8
(c1 (x1 ) → e1 (x1 ));
...
cn (xn ) → en (xn );
Stetigkeit von fix
Sei D eine cpo mit ⊥. Der kleinste-Fixpunkt-Operator fix : [[D → D] → D]
ist definiert als
G
fix f =
f n (⊥)
n
14
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Dies kann man auch schreiben als
f ix =
G
λf.f n (⊥)
n
Denn λf.⊥ ≤ λf.f (⊥) ≤ . . . ist eine aufsteigende Kette in [[D → D] →
D]. Da wir gezeigt haben, dass [[D → D] → D] eine cpo ist, existiert die
kleinste obere Schranke, und diese ist auch in [[D → D] → D]. Diese kleinste
obere Schranke ist gerade fix: denn
G
G
fix f =
f n (⊥) = ( λx.xn (⊥))f
n
n
Also gilt:
fix ∈ [[D → D] → D].
2
Eine Metasprache und Stetigkeit
Die Idee einer Metasprache ist eine allgemeine Möglichkeit, Funktionen auf
cpos als stetig nachzuweisen, solange sie sich mittels Lambda-Ausdrücken
darstellen lassen; und zwar ohne jedesmal einen expliziten Beweis führen zu
müssen. Dies soll helfen, für verschiedene Varianten von Lambda-Notationen
bzw. Lambda-Kalkülen die Stetigkeit von Ausdrücken aus allgemeineren
Prinzipien herzuleiten, anstatt diese jeweils extra nachzurechnen. Damit
kann man dann etwas leichter eine denotationale Semantik für eine gegebene Programmiersprache definieren, da man die Stetigkeit aller Ausdrücke
nicht gesondert nachweisen muß.
Wir nehmen an, dass es cpo’s gibt, so dass wir auch die neu konstruierten
cpo’s verwenden dürfen.
E ::= Di | E1 × . . . × En | [E1 → E2 ] | [E1 , . . . , En ] | E⊥
2.1
Syntax für die Lambda-Meta-Sprache
Für Ausdrücke:
• ci Konstanten für (alle) Elemente in einer festen cpo.
• Konstruktoren cE für bestimme cpo’s. Wir nehmen auch an, dass die
Summen-cpo’s aus cpo’s gebildet werden, die mit Konstruktoren verkleidet sind.
• Funktionen, die fest an eine bestimmte cpo’s gebunden sind: Projektionen, Einbettungen
• Generische Konstanten, wie apply, curry, fix, strict.
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
15
•
e ::= x | λx.e | (e1 e2 ) | (slet x = e1 in e2 )
| (case e of(cE,1 (x1 ) → e1 ); . . . ; (cE,n (xn ) → en ))
Analog zum Typcheck für die einfach getypte Lambda-Notation gilt auch
hier, dass nur solche Ausdrücke sinnvoll sind, für die man eine sinnvolle
Interpretation als Funktion auf einer (konstruierten) cpo herleiten kann.
Diese ist analog zum hergeleiteten Typ.
Die Bedingungen, die dann gelten müssen, sind:
• Variablen haben im Geltungsbereich stets die gleiche Zuordnung
• Konstanten haben ihre feste Zuordnung zur cpo,
• Jedes Vorkommen einer generische Konstanten hat ebenfalls eine
zulässige Belegung. D.h. man kann so tun, als gäbe es für jeden (sinnvollen Typ) eine generische Konstante dieses Typs.
• (e1 e2 ): e1 : [D → E], e2 : D und (e1 e2 ) gehört zur cpo E
• (slet x = e1 in e2 ): x gehört zur cpo D, e1 zur cpo D⊥ ; e2 und
(slet x = e1 in e2 ) gehören zur gleichen cpo E
• (case e of(cE,1 (x1 ) → e1 ); . . . ; cE,n (xn ) → en ): Der Ausdruck e gehört
zur cpo E, die Variable xi gehört zur Ei , cE,i ist der Konstruktor, der
Ei versteckt. E = [E1 , . . . , En ]. Der case-Ausdruck gehört zur cpo E.
Wir gehen davon aus, dass sinnvolle Ausdrücke genau diejenigen sind, für
die man eine Belegung aller Unterausdrücke mit cpo’s angeben kann, so dass
obige Bedingungen erfüllt sind. Dies ist nicht nur zufällig genauso wie im
einfach getypten Lambdakalkül.
2.2
Stetigkeit von Funktionen in der Metasprache
Hier braucht man wieder die Begriffe wie freie Variablen, gebundene Variablen, die wir hier voraussetzen.
Die Konstruktion von syntaktischen Ausdrücken ergibt i.a. Unterausdrücke, die freie Variablen enthalten. Dies muß in die Betrachtungen einfließen. Dazu benötigen wir eine angepaßte Definition der Stetigkeit eines
Ausdrucks mit freien Variablen.
Definition 2.1
1. Sei e ein Ausdruck, und seien x1 , . . . , xn die freien Variablen in e. Sei
x eine Variable. Dann ist e stetig in der Variablen x, wenn für alle
zulässigen Belegungen xi : Di mit di ∈ Di für xi 6= x gilt, dass
λx.e[di /xi | i = 1, . . . , n, xi 6= x] stetig ist.
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
16
2. Ein Ausdruck e ist stetig in seinen Variablen, wenn e stetig in allen
freien Variablen ist.
Dies ist unabhängig von der Reihenfolge der gewählten Variablen in der
Definition, da wir einen Ausdruck e : E als Funktion mit den freien Variablen
{x1 : D1 , . . . , xn : Dn } als Funktion von D1 × . . . × Dn → E ansehen können,
und bereits gezeigt haben, dass eine Funktion stetig ist, gdw. sie in jedem
Argument separat stetig ist.
Wir gehen jetzt die Konstrukte einzeln durch und argumentieren, dass
alle gebildeten Ausdrücke stetig sind:
Variablen x ist stetig in x, denn für jede cpo D, die man x zuordnet, ist
λx : D.x als Identität auf D stetig. x ist auch stetig in y für y 6= x, da
konstante Funktionen stetig sind. d.h ist stetig in seinen Variablen.
Generische Konstanten apply : [[D → E] × D → E], wobei D, E cpo’s
sind (polymorph)
fix: [D → D] → D
curry: (F × D → E) → (F → [D → E])
Diese sind alle stetig, wie bereits gezeigt. Da diese Funktionen keine
freien Variablen enthalten, gilt, dass sie stetig in allen Variablen sind.
Tupelbildung (e1 , . . . , en ):
Wenn alle ei stetig in allen Variablen sind, dann ist (e1 , . . . , en ) ebenfalls stetig in allen Variablen: Sei eine zulässige cpo-Belegung für das
Tupel gegeben. Diese ist auch eine für jedes ei . Da alle λx.ei [dy /y | x 6=
y] stetig sind, gilt das auch für λx.(e1 , . . . , en )[dy /y|x 6= y] (dies folgt
aus den Betrachtungen über Tupelbildung von Funktionen Also ist
(e1 , . . . , en ) stetig in seinen Variablen
Anwendung Sei e ein Ausdruck. Betrachte zunächst (apply(e1 , e2 )) wobei
apply eine generische Konstante ist. Dann gilt: wenn (e1 , e2 ) stetig ist,
dann auch (apply (e1 , e2 )).
Sei eine Belegung von (apply(e1 , e2 )) gegeben.
Dann ist dies auch eine gemeinsame Belegung von apply und (e1 , e2 ).
Sei außerdem (e1 , e2 ) stetig in x. Die cpo von (e1 , e2 ) wurde konstruiert
als (D → E) × D.
Dann ist λx.((e1 , e2 )[dy/y|y 6= x]) stetig und in [F → (D → E) × D]
Dann ist auch apply ◦ λx.(e1 , e2 )[dy /y] stetig.
Offenbar gilt: apply ◦ λx.(e1 , e2 )[dy /y] = λx.apply(e1 , e2 )[dy /y].
Denn (apply ◦ λx.(e1 , e2 )[dy /y](a) = (apply(e1 , e2 )[dy /y, a/x]
= (e1 , e2 )[dy /y, a/x] = (λx.apply(e1 , e2 )[dy /y])(a)
Damit ist apply(e1 , e2 ) stetig.
Offenbar ist apply(e1 , e2 ) = (e1 e2 ). Also ist Anwendung stetig.
17
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Abstraktion λz.e ist stetig in allen Variablen
Dazu betrachte λx.λz.e[dy /y | y 6= x, z].
Diese Funktion ist stetig, wenn x keine freie Variable in λz.e ist. Wenn
x eine freie Variable in λz.e ist, dann ist e[dy /y | y 6= x, z] als Funktion
von D1 × D2 → E stetig, wobei x, z die Variablen sind mit x : D1 und
z : D2 . Dann können wir diese Funktion transformieren mit curry:
curry(λ(x, z).e) = λx.λz.e[dy /y|y 6= x, z].
Da curry als stetig bereits nachgewiesen ist, ist damit die Funktion
λx.λz.e[dy /y|y 6= x, z] stetig.
Dies gilt für alle x, also ist λz.e stetig in seine Variablen
slet: (slet x = e1 in e2 ): Bedingung an eine Belegung mit cpo’s ist :
e1 : D⊥ , x : D, e2 : E. e2 ist stetig in allen Variablen, insbesondere ist
(λx.e2 ) stetig; damit auch die geliftete Funktion (λx.e2 )∗ . Dies haben
wir bereits bei der Betrachtung zu lifting gesehen. Da Anwendung
ebenfalls eine Konstruktion ist, die zu stetigen Funktionen führt, gilt:
(λx.e2 )∗ (e1 ) ist stetig in allen Variablen. Denn es gilt:
(slet x = e1 in e2 ) = (λx.e2 )∗ (e1 )
case: (case e of (cE,1 (x1 ) → e1 ); . . . ; (cE,n (xn ) → en ))
Hier muss die Belegung mit cpo’s so sein, wie oben beschrieben:
(e gehört zur cpo E, die Variable xi gehört zur cpo Ei , cE,i ist der
Konstruktor, der Ei versteckt. E = [E1 , . . . , En ].
Diese Konstruktion ist stetig, da
(case e of (cE,1 (x1 ) → e1 ); . . . ; (cE,n (xn )
[λx1 .e1 , . . . , λxn .en ] e definiert ist.
Damit sind auch alle
(if a then b else c ) stetig.
syntaktischen
→
en ))
Konstruktionen
als
wie
Fix : ist offenbar auch stetig, wie oben schon gezeigt. In Sprachen kommt
das oft als expliziter Rekursions-Operator vor, manchmal in der Syntax
µx.s
Zusammenfassend gilt, dass alle mittels der Metasprache bildbaren Ausdrücke stetig sind, insbesondere alle geschlossenen Ausdrücke.
18
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
3
PCF: Programming Language for Computable
Functions
Diese Sprache wurde von Dana Scott 1969 (LCF) eingeführt, um Grundlagenuntersuchungen an funktionalen Sprachen durchzuführen. PCF ist eine
einfach getypte funktionale Sprache, für die wir mit unseren Mitteln eine
denotationale Semantik mittels Konstruktionen über cpo’s angeben können.
PCF hat als Basisdatentypen natürliche Zahlen und die beiden Wahrheitswerte True, False. Einige wenige Funktionen sind schon vorhanden.
3.1
Syntax
Typen:
τ ::= num | Bool | τ → τ
Ausdrücke:
E ::= True | False |
0 | 1 | 2 | ... |
pred E | succ E, |
zero? E |
(if E then E else E ) |
x | λx :: τ.E | (E E) |
µx :: τ.E
Alle zulässigen Ausdrücke sind einfach (monomorph) getypt. D.h.
diese haben einen Typ, der keine Variablen enthält. Dieser Typ ist zudem
eindeutig.
In den Konstrukten λ, µ müssen die Variablen mit einem (monomorphen)
Typ versehen sein, damit man den Typ der Ausdrücke angeben kann.
Einige Typen von Ausdrücken:
0, 1, 2, . . .:
num
zero?:
num → Bool
if−then−else−:: Bool → α → α → α für alle α.
D.h. das if-then-else kann man als generisches Konstrukt ansehen, das
für jeden Typ τ zu einem extra if-then-elseτ instanziiert wird.
Der neue Operator µ ist der Fixpunktoperator.
Typregel für µ ist:
λx.E : τ → τ
(µx : τ.E) : τ
3.2
Operationale Semantik von PCF
Die operationale Semantik wird zunächst als “big-step“ Semantik angegeben. Mit v bezeichnen wir einen Wert und mit w einen Wert, oder einen λoder µ-Ausdruck. Die Regeln für die Reduktion →op sind:
19
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
e1 →op True; e2 →op v
if e1 then e2 else e3 →op v
e1 →op False; e3 →op v
if e1 then e2 else e3 →op v
s →op (λx.e) e[a/x] →op w
(λx.e) a →op w
(e[(µx.e)/x]) →op w
(µx.e) →op w
t →op pred; e →op n und n > 0
t e →op (n − 1)
t →op succ; e →op n
t e →op (n + 1)
t →op pred; e →op 0
t e →op 0
t →op zero?; e →op 0
t e →op True
t →op zero?; e →op n und n 6= 0
t e →op False
Als Reduktionsrelation (d.h. “small-step Semantik“) kann man das auch
folgendermaßen definieren:
if True then e2 else e3
if False then e2 else e3
pred n
pred 0
succ n
zero? 0
zero? n
(λx.e)a
(µx.e)
→
→
→
→
→
→
→
→
→
e2
e3
n−1
falls n > 0
0
n+1
True
False
falls n > 0
e[a/x]
(e[µx.e/x])
Betrachten wir noch Reduktionskontexte in PCF:
R ::= [] | R E | if R then E else E | pred R | succ R | zero? R
, und erlauben nur Reduktionen, die in einem Reduktionskontext stattfinden: d.h. nur R[s] → R[t] ist zulässig. Diese Reduktion im Reduktionskontext bezeichnen wir auch als Normalordnungsreduktion. Die transitive Hülle
∗
von → bezeichnen wir als →
−.
∗
Es gilt, dass →op ⊆ →
−.
Die Sprache PCF ist zwar einfach und monomorph getypt, aber es ist
leicht zu sehen, dass sie Turing-mächtig ist. D.h. man kann alle berechenbaren Funktionen definieren.
Definition 3.1 Sei t ein PCF-Ausdruck.
• t ist in Normalform gdw. t nicht mehr reduzierbar ist.
20
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
• t ist in WHNF (schwache Kopfnormalform): gdw. t ≡ λx.t0 oder t ist
ein Basiswert vom Typ num oder Bool.
Definition 3.2 Sei t ein geschlossener PCF-Ausdruck. Wenn ein t0 exi∗
stiert mit t →
− t0 , wobei t0 eine WHNF ist, dann schreiben wir t0 ↓.
3.3
Übersetzung PCF nach Haskell
Bevor wir die Eigenschaften von PCF genauer betrachten, geben wir
zunächst eine Übersetzung nach Haskell an. Wir wollen diese Übersetzung
nicht formal verifizieren. Diese Übersetzung soll nicht dazu dienen, PCF semantisch zu fundieren, sondern soll illustrieren, wie man für PCF schnell
einen Interpreter bauen kann. Außerdem kann man danach die Ausführung
von PCF-Ausdrücken und die Unterschiede zu Haskell schneller sehen.
Die übersetzten Funktionen sind zum Teil allgemeiner, d.h. in Haskell
haben sie einen polymorphen Typ statt eines monomorphen in PCF; sie
könnten Argumente zulassen, die in PCF verboten wären. Aber es soll folgendes erfüllt sein:
1. Übersetzte Ausdrücke sind wohlgetypt in Haskell.
∗
2. Sei t ein Ausdruck in PCF und t →
− n und n eine Zahl oder ein Boolescher Wert. Wenn wir die Übersetzung τ und die Reduktion in Haskell ebenfalls mit Index H andeuten, dann gilt für die Übersetzung:
∗
tH →
− H nH .
3.3.1
Übersetzung nach Haskell:
Typen: τ ::= num | Bool | τ → τ
Ausdrücke: Boolesche Werte und Zahlen kann man direkt übersetzen.
if-then-else und pred, succ ebenfalls direkt.
zero?H := iszero
(λx : τ.E)H := \x → EH
(µx : τ.E)H = fix(\x → EH )
Folgende Haskell-Definition werden für diese Übersetzung benötigt:
pred n
succ n
iszero x
fix f
=
=
=
=
if n > 0 then n-1 else 0
n+1
x == 0
f (fix f)
Man kann alternativ auch das let(rec)-Konstrukt zur Übersetzung des
µ verwenden:
(µx : τ.E)H = let x = EH in x
Die Fixpunktbildung entspricht offenbar der (einfachen) Rekursion.
21
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Beispiel 3.3 Addition mittels succ. Ein Versuch in funktionaler Notation:
add s t := if zero?(s) then t else succ (add (pred s) t)
In PCF sieht diese Funktion so aus:
add := µa.(λs, t.if zero? (s) then t else succ (a(pred s) t)
Die Haskell-Übersetzung dieser Funktion ist:
1. erste Variante
add = fix (\a -> (\s t -> if iszero s then t else succ (a (pred s) t)))
2. Zweite Variante
add =
let a =
in a
(\s t -> if iszero s then t else succ (a (pred s) t))
Beispiel 3.4 Multiplikation:
funktional:
mult s t := if zero?(s) then 0 else add t (mult (pred s) t)
in PCF:
mult := µm.λs, t.if zero?s then 0 else add t (m(pred s)t)
Haskell-Übersetzung:
1. mult = fix (\m -> (\s t -> if iszero s then 0
else add t (m (pred s) t)))
2. mult = let
m =
(\s t -> if iszero s then 0
else add t (m (pred s) t))
in m
4
Eigenschaften von PCF
Definition 4.1 Eine Relation → ist konfluent (bzw. Church-Rosser) gdw.
∗
∗
für alle s, s1 , s2 : wenn s →
− s1 und s →
− s2 , dann existiert ein weiterer Term
∗
∗
s3 , so dass s1 →
− s3 und s2 →
− s3 .
Satz 4.2 Die Reduktionsrelation → ist konfluent.
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
22
Beweis. Mit Methoden wie in (Barendregt)
2
Ohne den Fixpunktoperator ist diese Sprache nicht Turingäquivalent:
Es gilt, dass ohne Fixpunktoperator alle Reduktionen terminieren. Diese
Aussage benötigen wir auch später für die Adäquatheit der denotationalen
Semantik.
Folgende Aussage entspricht einer Variante des Progress-Lemmas.
Lemma 4.3 In PCF gilt: geschlossene Terme, die Normalformen vom Typ
Bool bzw. num sind, können nur die Basiswerte selbst sein, d.h. Zahlen oder
Wahrheitswerte.
Beweis. Wir zeigen dies mit Induktion nach der Größe der Terme:
Es gilt: Ein Term M in Normalform kann kein µ-Ausdruck enthalten, denn
jeder µ-Term ist reduzierbar, also nicht in Normalform.
Wir gehen die Konstruktionsmöglichkeiten durch.
• M ≡ 0, n, True, False: trivial.
• M ≡ pred M 0 , succ M 0 , zero? M 0 : Das gilt mit Induktion, da M 0
ebenfalls von Basistyp, und eine Ein-Schritt Reduktion ausreicht, um
den Ausdruck ebenfalls in einen Basiswert zu überführen.
• M ≡ if M0 then M1 else M2 : wenn dieser Term in Normalform ist,
dann ist auch M0 in NF, also True oder False. Dann kann man aber
reduzieren.
• M ≡ λx.M 0 : kann nicht sein, da dies vom falschen Typ ist.
• M
≡
(M1 M2 ): Dieser Term muß von der Form
0
((. . . (Mn Mn−10 ) . . . ..)M2 ) sein. Der Term Mn0 kann nur eine
Abstraktion sein. If-then-else ist nicht möglich, da wegen der Induktionshypothese ein inneres if-then-else nicht in NF sein kann. Damit
kann man aber β-reduzieren, und der Term ist nicht in Normalform.
2
Wir betrachten jetzt das Fragment P CF−µ : das ist die Sprache PCF
ohne den µ-Operator.
Definition 4.4 Definiere eine Menge Ttrm von P CF−µ -Termen als kleinste
Menge, die folgendes erfüllt:
1. Wenn M geschlossen ist und vom Typ num oder Bool, und alle Reduktionen, die von M ausgehen, terminieren, dann ist M ∈ Ttrm .
2. Sei M : τ1 → τ2 . Dann ist M ∈ Ttrm , wenn für alle geschlossenen
N : τ1 ∈ Ttrm gilt: (M N ) ∈ Ttrm .
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
23
Jetzt definiere noch die Menge Ttrm,v folgendermaßen:
Wenn M die freien Variablen x1 , . . . , xn hat, dann ist M ∈ Ttrm,v , wenn
σ(M ) ∈ Ttrm für alle σ = {xi → Ni | i = 1, . . . , n} wobei Ni ∈ Ttrm .
Offenbar gilt:
1. Sei M ∈ Ttrm . Dann terminieren alle von M ausgehenden Reduktionen.
2. M ∈ Ttrm,v gdw. für alle σ, die Ttrm - Terme für freie Variablen einsetzen und für alle geschlossenen N1 . . . Nk ∈ Ttrm , so dass
σ(M ) N1 . . . Nk von Basistyp ist : σ(M ) N1 . . . Nk hat nur terminierende Reduktionen.
Satz 4.5 Betrachte das Fragment P CF−µ der Sprache PCF. Für die Ausdrücke dieser Sprache gilt, dass sie in Ttrm,v enthalten sind. D.h. Man kann
jeden geschlossenen Term in P CF−µ nur endlich oft reduzieren.
Beweis. Mit Induktion nach der Größe der Terme.
• M ≡ x: trivial
• M ≡ 0, True, False, n: trivial, da alles in Normalform.
• M ≡ pred M 0 , succ M 0 , zero?(M 0 ): Betrachte σ(M 0 ). Nach Induktion terminiert σ(M 0 ). Wegen der operationalen Definitionen von
succ, pred, zero? gilt dies auch für σ(M ). Also ist M ∈ Ttrm,v .
• M ≡ if M0 then M1 else M2 Nehme an, dass Ni ∈ Ttrm sind und σ
Substitution, die nur Terme aus Ttrm einsetzt, so dass (σM ) N1 . . . Nn
geschlossener Term vom Basistyp ist.
Induktion zeigt, dass (σ M0 ) terminierend ist, ebenso (σM1 ) N1 . . . Nn
und (σM2 )N1 . . . Nn also ist auch (σM ) N N1 . . . Nn terminierend.
Dies gilt für alle terminierenden, geschlossenen Ni . Also gilt M ∈
Ttrm,v .
• M ≡ (L N ): Da L, N ∈ Ttrm,v nach Induktionshypothese, gilt für
alle Substitutionen σ die nur Terme aus Ttrm einsetzen, dass auch
σL, σN ∈ Ttrm . Wegen der Definition von Ttrm ist auch (σL (σN ))
terminierend, also ist (L N ) ∈ Ttrm,v .
• M = λx.M 0 : Zu zeigen ist: (λx.σM 0 )N ∈ Ttrm ist terminierend für
geschlossene N ∈ Ttrm,v und Substitutionen σ, die nur Terme aus
Ttrm einsetzen.
Da M 0 ∈ Ttrm,v nach Induktionshypothese, gilt s[N/x]M 0 ∈ Ttrm . Also
gilt das auch für σ(M 0 [N/x]). Damit ist M = λx.M 0 ∈ Ttrm,v .
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
24
2
Korollar 4.6
1. Alle Terme des µ-freien PCF-Fragments haben nur terminierende Reduktionen.
2. Alle Terme vom Basistyp im µ-freien PCF-Fragment reduzieren zu
einer Zahl, oder True, False.
Korollar 4.7 Der µ-Operator ist nicht im µ-freien PCF-Fragment definierbar. D.h dies ist eine echte Erweiterung.
5
Denotationale Semantik von PCF
Für jedes Konstrukt müssen wir angeben, wie syntaktische Objekte denotiert werden: Da PCF-Unterterme freie Variablen enthalten können, benötigen wir eine Umgebungsvariable. Diese Umgebung ist eine partielle Funktion von Variablen in zugehörige Werte. Wir nehmen an, dass Variablen
mit einem Typ markiert sind. Wir nehmen auch an, dass die Ausdrücke
entsprechend dem einfach getypten Lambda-Kalkül getypt sind.
Als Domains verwenden wir cpo’s, die wir im Vorlesungsteil zu cpo’s
eingeführt und ausführlich behandelt haben. Die verwendeten Notationen
sind dort erklärt.
Die Denotationen der Typen ergeben sich rekursiv folgendermaßen:
[[num]]
= IN⊥
geliftete natürliche Zahlen
[[bool]]
= {True, False}⊥ geliftete zweielementige Menge
[[τ1 → τ2 ]] = [[[τ1 ]] → [[τ2 ]]]⊥
stetige Funktionen, geliftet
Wir benutzen hier geliftete Funktionenräume, d.h. es gibt einen extra
hinzugefügten Wert ⊥, der verschieden ist von der Funktion die stets ⊥ liefert. Der Funktionenraum vor dem Liften hat bereits ein kleinstes Element:
es ist die Denotation von λx.⊥. Die Anwendung von ⊥ als Funktion auf ein
Argument ergibt in D stets ebenfalls ⊥. Das gleiche gilt für die Denotation
von λx.⊥, d.h. die Extensionalität gilt nicht mehr in D: es gibt verschiedene
Objekte, die als Funktionen gleich sind.
Beachte, dass in anderen Artikeln auch stetige Funktionenräume benutzt
werden ohne Lifting.
Cartesische Produkte werden nicht benötigt, da es keine Datenkonstruktoren, insbesondere keine Tupel in PCF gibt. Diese kann man verwenden,
wenn man PCF mit Tupeln erweitern will.
Die zugehörigen Denotationen für geschlossene Terme M :: τ sind dann
jeweils aus der Denotation [[τ ]] des Typs τ , d.h. [[M ]] ∈ [[τ ]]. Bei offenen
25
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Termen gilt das ganz ähnlich, nur muß man noch Einsetzungen für die freien
Variablen hinzufügen.
Im folgenden lassen wir die Typisierungsbezeichnungen teilweise weg.
Trotzdem nehmen wir an, dass diese vorhanden sind.
Definition 5.1 (denotationale Semantik von PCF)
ρ ist eine Umgebung, die Variablen Werte aus der richtigen Menge zuordnet.
ρ(x :: τ ) ∈ [[τ ]]
Diese Umgebung wird benötigt, damit man Unterausdrücke, die freie Variable enthalten, behandeln kann. Ausdrücke ohne freie Variablen (geschlossene Ausdrücke) haben eine denotationale Semantik, die unabhängig von
einer Umgebung ist.
Die Vorstellung ist: Für alle globalen Werte (die in ρ) gilt die Definition
bereits. Wir benutzen λ auch auf der Seite der Denotationen, aber notieren
es dort als λ
[[x]] ρ
[[n]] ρ
[[pred M ]] ρ
=
=
=
=
=
[[succ M ]] ρ
=
=
[[if M then L1 else L2 ]] ρ =
=
=
[[(M N )]] ρ
=
[[λx.M ]] ρ
=
[[µx.M ]] ρ
=
ρ(x)
n
([[M ]] ρ) − 1
0
⊥
([[M ]] ρ) + 1
⊥
⊥
[[L1 ]] ρ
[[L2 ]] ρ
([[M ]] ρ) ([[N ]] ρ)
λy.([[M ]](ρ[y/x]))
fix(λy.([[M ]](ρ[y/x]))
(hier müssen M und x
wenn
wenn
sonst
wenn
wenn
wenn
wenn
wenn
([[M ]] ρ) > 0
([[M ]] ρ) = 0
([[M ]] ρ) ≥ 0
([[M ]] ρ) = ⊥
[[M ]] ρ = ⊥
[[M ]] ρ = True
[[M ]] ρ = False
gleichen Typ haben)
Um zu sehen, dass die Definitionen alle stetig sind, wenden wir die Überlegungen zur Meta-Notation an. Die einfache Typisierung aller Ausdrücke
stellt sicher, dass es jeweils zulässige Belegungen mit cpo’s gibt.
• Alle Konstrukte sind stetig: succ, pred und zero? sind auf gelifteten,
diskreten cpo’s definiert
• Applikation, Abstraktion, Fixpunktbildung sind ebenfalls stetig als generische Konstanten.
• if-then-else ist als case - Konstrukt über der gelifteten Summe ebenfalls stetig.
∗
Satz 5.2 Seien M und N PCF-Ausdrücke. Wenn M →
− N , dann gilt:
[[M ]] ρ = [[N ]] ρ für alle ρ .
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
26
Beweis. Wir zeigen dies für die Einschrittrelation →. Dann gilt das auch für
die transitive Hülle. Wir betrachten dabei jeweils den Unterausdruck, der
unmittelbar reduziert wird (den Redex).
• pred n → n − 1:
[[pred n]] ρ = ([[n]] ρ) − 1 = n − 1.
Analog für pred 0, succ n, zero? n.
• (if True then L1 else L2 ) → L1 :
[[if True then L1 else L2 ]] ρ = [[L1 ]] ρ für alle ρ.
• Analog: False-Fall
• (λx.M )N → M [N/x]:
[[(λx.M )N ]] ρ = ([[(λx.M )]] ρ)([[N ]] ρ)
= (λy.[[M ]] ρ[x → y])([[N ]] ρ)
= [[M ]] ρ[x → ([[N ]] ρ)]
= [[M [N/x]]] ρ( Induktion nach der syntaktischen Struktur von M )
• (µx.M ) → (M [(µx.M )/x];
=
=
=
=
=
[[(µx.M )]] ρ
fix(λy.([[M ]](ρ[x → y])))
(λy.([[M ]](ρ[x → y]))(f ix(λy.([[M ]](ρ[x → y])))
(λy.([[M ]](ρ[x → y])))([[(µx.M )]] ρ)
([[M ]](ρ[x → ([[(µx.M )]] ρ)])))
[[(M [(µx.M )/x])]] ρ
2
Die andere Richtung, nämlich dass der semantische Wert etwas über
die Reduktion aussagt, ist etwas komplizierter. Die gewünschte Aussage für
geschlossene Ausdrücke M, N ist: Wenn [[M ]] ∅ = [[N ]] ∅ und N eine WHNF
∗
ist, dann auch M →
− N . Dies ist zuviel verlangt, es gilt für Konstanten wie
Zahlen und die Booleschen Konstanten, aber nicht für Abstraktionen, denn
Lambda-abstraktionen in WHNF sind nicht notwendig auch in Normalform.
Beachte, dass durch das Liften der Funktioneräume [[λx.⊥]] 6= [[⊥]], was
dazu passt, dass λx.⊥ eine WHNF hat, während ⊥ keine WHNF hat.
Das folgende werden wir nachweisen:
∗
Wenn [[M ]] ∅ = m ist, dann gilt M →
− m.
Zunächst zeigen wir, wie wir alle Fixpunktausdrücke approximieren
können.
Sei M = µx.M 0 ein µ- Ausdruck, dann definiere:
27
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Semantische Seite:
Betrachte d = fix(λy.([[M 0 ]](ρ[x → y])) Offenbar ist dies ein lub der Kette
dn := en ⊥ wobei e = (λy.([[M 0 ]](ρ[x → y]))
Syntaktische Seite:
M0
:= µx.x
M k+1 := (λx.M 0 )Mk
Lemma 5.3 Es gilt: [[Mk ]] ρ = dk
Beweis. Induktion nach k:
[[M0 ]] ρ = ⊥ = d0 .
Angenommen, es gilt bereits für k.
[[M k+1 ]] ρ = [[(λx.M 0 )M k ]] ρ = ([[(λx.M 0 )]] ρ)([[M k ]] ρ)
= ([[(λx.M 0 )]] ρ)dk = λy.([[M 0 ]](ρ[x → y]))dk = e(ek ⊥) = ek+1 ⊥ = dk+1 . 2
Lemma 5.4 Sei N ein geschlossener PCF-Term, so dass [[N ]] = n eine Zahl
∗
ist und alle µ-Ausdrücke in N von der Form µx.x sind. Dann gilt N →
− n.
Beweis. Angenommen, das ist falsch. Dann gibt es einen Term N :: num mit
[[N ]] = n, alle µ-Ausdrücke in N sind von der Form µx.x und die Normalordnungsreduktion für N terminiert nicht. Da alle Reduktionen im µ-freien
Fragment von PCF terminieren, muß die Normalordnungsreduktion irgendwann einen der Unterterme µx.x reduzieren. Wir nehmen einen Term, für
den das gilt, und der eine kleinste Anzahl von Normalordnungsreduktion
benötigt, bis das erste mal µx.x reduziert wird. Unter diesen wählen wir
den kleinsten Term. Offenbar können wir annehmen, dass die erste Normalordnungsreduktion bereits einen Term µx.x reduziert. Da der Term µx.x ein
Normalordnungsredex ist, kann er nur in folgendem Kontext auftauchen:
pred(µx.x), succ(µx.x), zero?(µx.x), if (µx.x) then e1 else e2 , ((µx.x)e).
In jedem dieser Fälle können wir den Term verkleinern, indem wir den Redex
durch µx.x ersetzen. Die denotationale Semantik des Terms bleibt gleich, da
pred ⊥, succ ⊥, zero? ⊥, if ⊥ then e1 else e2 , (⊥ x) jeweils als Denotation
wieder ⊥ haben. Die operationale Semantik bleibt ebenfalls gleich, d.h. die
Normalordnungsreduktion terminiert auch für den neu konstruierten Term
nicht. Der konstruierte Term ist jedoch kleiner geworden. D.h. der Term
selbst muß µx.x sein. Dessen Denotation ist aber ⊥, im Widerspruch zur
Annahme.
2
Satz 5.5 Sei N ein geschlossener PCF-Term, so dass [[N ]] = n eine Zahl
∗
ist. Dann gilt N →
− n.
Beweis. Induktion nach der Anzahl der echten µ-Ausdrücke in N , wobei wir
die Ausdrücke µx.x nicht mitzählen.
28
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Basis. Wenn N keine echten µ-Ausdrücke enthält, dann terminiert die
Reduktion von N mit einem n0 nach obigem Lemma. Wir haben bereits gezeigt, dass dann n = n0 sein muß.
Induktionsschritt.
Wähle in N einen innersten echten µ-Ausdruck M aus, der selbst keine echten µ-Ausdrücke mehr enthält und definiere Nk als die Terme, die entstehen,
wenn für N jeweils M k (wie oben) eingesetzt wird. F
Wir übernehmen dieFBezeichnungen wie oben. Da k dk = d (für alle ρ)
ist, gilt somit auch k [[Nk ]] = [[N ]] = n. Hierbei können wir F
die Stetigkeit verwenden, die wir für die Metasprache gezeigt haben. Da k [[Nk ]] =
[[N ]] = n, muß die Folge [[Nk ]] für k = 1, 2, . . . folgende Form haben:
⊥, ⊥, . . . , ⊥, n, n . . . . Also gibt es ein k0 , so dass [[Nk0 ]] = [[N ]] = n. Nk0
hat einen µ-Ausdruck weniger als N , dann gilt die Induktionshypothese,
∗
und wir können schließen, dass Nk0 →
− n. Somit gibt es auch eine Normal∗
ordnungsreduktion Nk0 →
− n. Da der Term M k0 im Innern einen nichtterminierenden Term enthält, wurde für diesen die WHNF nicht berechnet.
Damit gilt: Man kann den inneren Term ⊥ wieder durch M ersetzen und
∗
erhält Nk00 mit Nk00 →
− n. Da aber Nk00 durch Reduktion aus N hergeleitet
∗
∗
∗
werden kann, d.h. N →
− Nk00 →
− n, gilt insgesamt N →
− n.
2
Korollar 5.6 Sei N ein geschlossener PCF-Term vom Typ num. Dann gilt
[[N ]] = ⊥ gdw. N keine terminierende Reduktion hat.
Was hier noch zu zeigen ist, wäre die Aussage:
Wenn N geschlossen ist und [[N ]] 6= ⊥, dann gilt N ↓
6
Volle Abstraktion und Gleichheit
Gleichheit von Funktionen kann man jetzt auf zwei Arten definieren:
• Die denotationale Gleichheit:
s =d t gdw. [[s]] = [[t]]
In dieser Semantik gilt:
29
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
die Funktion por (paralleles “or“) mit
por
True
True
False
False
⊥
True
False
⊥
⊥
True
False
True
False
True
⊥
⊥
False
⊥
=
=
=
=
=
=
=
=
=
True
True
True
False
True
True
⊥
⊥
⊥
ist monoton und stetig, also im Modell enthalten, aber es gibt keinen
PCF-Ausdruck, der die Denotation von por hat.
• kontextuelle Gleichheit. Ein Programmkontext ist ein Ausdruck C
mit einem Loch an einer Stelle, an der ein Unterterm stehen darf. Wir
schreiben dies als C[].
Wir definieren s =c t gdw. für alle Programmkontexte C[] gilt: C[s]↓
gdw. C[t]↓.
Diese kontextuelle Gleichheit vergleicht Terme anhand ihres Terminierungsverhaltens in allen Programmen. D.h. wenn sich s, t nicht unterscheiden lassen durch Einsetzen in einen Programmkontext, dann
werden sie als gleich betrachtet.
Aussage 6.1 Es gilt s =d t =⇒ s =c t (Adäquatheit der denotationalen
Semantik)
Beweis. Wenn s =d t, dann gilt für alle Programmkontexte C[] auch C[s] =d
C[t]. Wenn [[C[s]]] nicht ⊥ ist, dann ist auch [[C[t]]] nicht ⊥. Dann gilt für
beide Terme C[s] und C[t], dass diese konvergieren. Damit ist s =c t.
2
Übungsaufgabe 6.2 Definiere in PCF die logischen Funktionen wie
not, and, or.
Lemma 6.3 In PCF gilt für alle Funktionen f : τ1 → . . . τn → B (Booloder
num). Die Funktion f ist entweder konstant oder strikt in einem Argument.
Beweis. Annahme: Die Funktion f sei nicht konstant. Dann betrachte eine
Reduktion in Normal-Ordnung wobei der Term (f t1 . . . tn ) reduziert wird.
Wenn die Unterausdrücke ti nicht reduziert werden, dann wäre f konstant,
also wird mindestens ein Term ti zu WHNF reduziert. Da der Index i des
30
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
reduzierten ti unabhängig vom Ausdruck ti ist, gibt es einen Index i, so dass
ti reduziert wird. Die Funktion f ist damit strikt im i-ten Argument. 2
Wir betrachten die Funktion
F = λxλf. if(and
(f True ⊥)
(f ⊥ True)
not (f False False))
then x
else True
Lemma 6.4 Es gilt (F True) =c (F False)
Beweis. Wir nutzen das obige Lemma aus. Wir wenden (F True)
und (F False) auf eine Funktion f vom Typ Bool → Bool →
Bool an. Wenn diese konstant ist, dann kann der Ausdruck
(and (f True ⊥) (f ⊥ True) (not (f False False))) nicht zu True auswerten. Damit stimmen (F True f ) und (F False f ) überein. Wenn f nicht
konstant ist, dann ist f strikt im ersten oder im zweiten Argument, und
das Ergebnis ist jeweils ⊥.
2
Wenn wir die beiden Funktionen (denotational) semantisch betrachten,
dann gilt:
Lemma 6.5 Es gilt (F True) 6=d (F False)
Beweis. Wende die Denotationen auf por an. Dann gilt: (F True por) =
True und (F False por) = False.
2
Definition 6.6 Eine denotationale Semantik ist voll abstrakt, wenn die
kontextuelle Gleichheit mit der denotationalen Gleichheit übereinstimmt.
Korollar 6.7 Unsere denotationale Semantik von PCF ist nicht voll abstrakt.
Begründung: Die beiden Ausdrücke (F True) und (F False) sind ein
Gegenbeispiel, denn (F True) =c (F False), aber (F True) 6=d (F False).
Abhilfe:
Alternative 1: Um eine voll abstrakte denotationale Semantik zu erhalten,
fügt man in der Syntax parallele Operationen hinzu: Es gilt, dass PCF
+ por als voll abstrakte Semantik genau die bisherige denotationale
Semantik hat. Allerdings sagt diese Semantik dann nichts mehr aus
über Eigenschaften sequentieller Programme.
31
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Alternative 2 Man verkleinert die Grundmenge, die der denotationalen
Semantik zur Verfügung steht. Dies ergibt eine denotationale Semantik, die auf der operationalen basiert, und die aus allen Ausdrücken
erzeugt wird.
Problem hierbei: Gibt es ein effektives Konstruktionsverfahren für diese Grundmenge?
Leider gelten selbst bei endlichem Grundbereich negative Aussagen:
• Es gibt kein effektives Konstruktionsverfahren, wenn man nur Bool
als Grundmenge nimmt?
Das “Lambda-Definierbarkeits-Problem für endliches PCF
ist unentscheidbar“
• Die kontextuelle Gleichheit ist unentscheidbar, auch wenn man nur
[[Bool]] als Grundmenge nimmt. D.h. man kann von zwei gegebenen
Ausdrücken s, t in PCF, die keinen Bezug zu Zahlen haben, d.h. nur
Boolesche Konstanten und Typen, aber natürlich auch Funktionstypen
verwenden, nicht algorithmisch entscheiden, ob diese kontextuell gleich
sind.
7
Satz von Bekić
Man sieht: In PCF fehlen Datenkonstruktoren (Produkttypen, semantisch:
Kreuzprodukte.)
Programmiertechnisch bewirkt dies unter anderem, dass wir keine Datenstrukturen auf einfache Weise mit Konstruktoren aufbauen können: keine
Paare, Listen u.ä. Außerdem scheint die einfache Möglichkeit zu fehlen, verschränkt rekursive Funktionen zu definieren.
Wir gehen nochmal zurück zu cpo’s und wollen jetzt zeigen, dass es diese
Möglichkeit doch gibt, allerdings ist diese etwas versteckt.
Sei D cpo mit ⊥ und F : D → D eine stetige Funktion. Dann existiert
ein kleinster Fixpunkt fix(F ). Für diesen gilt:
(fix1) F (d) ≤ d =⇒ fix(F ) ≤ d
von F )
(fix(F ) ist auch kleinster Präfixpunkt
(fix2) F (fix(F )) = fix(F )
Der Satz von Bekić sagt etwas aus über die Existenz von Fixpunkten auf
Kreuzprodukten und wie diese aus koordinatenweisen Fixpunkten berechnet
werden können.
Satz 7.1 (Bekić)
Seien D, E cpo’s mit ⊥ und F : D × E → D und G : D × E → D stetige
Funktionen. Der kleinste Fixpunkt von hF, Gi : D × E → D × E läßt sich
darstellen als hfˆ, ĝi wobei
32
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
fˆ = µf.F (f, µg.G(µf.F (f, g), g))
(= µf.F (f, ĝ))
ĝ = µg.G(µf.F (f, g), g)
Beweis.
1. Ist Fixpunkt: fˆ ist Fixpunkt von µf.F (f, ĝ) , also ist fˆ = F (fˆ, ĝ).
ĝ = G(µf.F (f, ĝ), ĝ) = G(fˆ, ĝ).
Damit ist hF, Gi(fˆ, ĝ) = (fˆ, ĝ).
2. Seien (f0 , g0 ) kleinere Fixpunkt von hF, Gi. Dann gilt f0 ≤ fˆ und
g0 ≤ ĝ.
Wir zeigen die umgekehrten Ungleichungen. Da f0 = F (f0 , g0 ), gilt
auch µf.F (f, g0 ) ≤ f0 .
G ist monoton, also gilt: G(µf.F (f, g0 ), g0 ) ≤ G(f0 , g0 ) = g0
Also gilt ĝ ≤ g0 . Denn ĝ ist der kleinste Präfix-Punkt von
λg.G(µf.F (f, g), g).
Monotonie von F zeigt jetzt, dass : F (f0 , ĝ) ≤ F (f0 , g0 ) = f0 . Damit
ist f0 ein Präfixpunkt von λf.F (f0 , ĝ). Also ist fˆ ≤ f0 .
2
Da wir den Satz auch für schwächere Strukturen (partielle Ordnungen,
aber keine cpo’s) benutzen wollen, formulieren wir den Satz mit schwächeren
Voraussetzungen:
Satz 7.2 (Bekić) (Version 2) Seien D, E partielle Ordnungen mit ⊥ und
F : D ×E → D und G : D ×E → D monotone Funktionen. Wir nehmen an,
dass die kleinsten Fixpunkte in folgenden Formeln existieren. Der kleinste
Fixpunkt von hF, Gi : D × E → D × E läßt sich darstellen als hfˆ, ĝi wobei
fˆ = µf.F (f, mg.G(µf.F (f, g), g))
(= µf.F (f, ĝ))
ĝ = µg.G(µf.F (f, g), g)
Es gibt auch eine symmetrische Form dieses Satzes:
fˆ = µf.F (f, µg.G(f, g))
ĝ = µg.G(µf.F (f, g), g)
Offenbar ist es kein Problem, den Satz von Bekić auch für mehrere Variablen zu formulieren und zu zeigen.
In der Anwendung haben wir meist F : D → D → D und G : D → D →
D. Das erste entspricht einer rekursiv definierten Funktion f , das zweite
Argument der rekursiv definierten Funktion g.
Ein Programmkonstrukt, das sich auf diesen Satz stützen kann, ist das
letrec:
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
33
(letrec x = s, y = t in e) x, y dürfen in s, t und e vorkommen und
sind dort durch das letrec gebunden.
Die Anwendung ergibt dann folgende Semantik:
ŷ = µy.t[(µx.s)/x]
x̂ = µx.s[ŷ/y]
Somit könnten wir mittels Bekić’s Satz die Sprache PCF auch um ein
letrec mit mehreren Variablen erweitern. Der Satz von Bekić zeigt: diese
Erweiterung fügt keine neue Ausdruckskraft hinzu:
8
Einfach getypte Kombinatorsprache: P CFK
Da Haskell im Prinzip auch ohne die Konstrukte λ, µ, letrec auskommen
kann, ist es interessant, die Beziehung von PCF zu einer Kombinatorvariante
von PCF zu untersuchen. Wir nennen dies funktionale Sprache P CFK .
Diese hat die gleichen Typen wie PCF. Ebenso die gleichen Basiskonstanten und Funktionen. Es gibt aber kein λ und kein µ. Stattdessen gibt
es rekursive Definitionen von Kombinatoren. Den Kombinatoren muß ein
monomorpher Typ zugewiesen werden.
Definitionen sind von der Form:
(hnamei : hT ypi) x1 . . . xn = e
wobei e andere Kombinatornamen enthalten darf. Alle freien Variablen von
e sind in {x1 , . . . , xn } enthalten.
main: ist der (reservierte) Name des auszuwertenden Ausdrucks.
Es gilt: Diese Sprache P CFK ist äquivalent zu PCF.
Den Begriff der Äquivalenz müssen wir genauer spezifizieren:
Es gibt eine natürliche Übersetzung von PCF nach P CFK , so dass folgendes
gilt:
Die Übersetzung erhält den Typ und es gilt:
Zu jedem Ausdruck e : num in PCF existiert ein Programm Pe in P CFK , so
dass maine eine Normalform n hat, gdw. e die Normalform n hat.
8.1
8.1.1
Übersetzung
P CF → PCFK
Gegeben ein geschlossener Ausdruck e in PCF. Zunächst mache alle gebundenen Variablennamen disjunkt, und achte darauf, dass dies im Algorithmus
zu jeder Zeit eingehalten wird.
Von unten her verwandle Lambda-Ausdrücke und µ-Ausdrücke in
Kombinator-Ausdrücke.
34
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
Fall: ein innerer Ausdruck ist ein Lambda-Ausdruck. Betrachte einen maximalen Ausdruck t. D.h. einen solchen, der nicht selbst genau der Rumpf
eines Lambda-Ausdrucks ist.
• Wenn t keine freien Variablen enthält, dann erzeuge eine Superkombinatordefinition: λx1 , . . . xn .e wird zu neuerkomb x1 . . . xn = e. Ersetze
den Lambda-Ausdruck durch
• Wenn t freie Variablen enthält, dann verwende Lambda-Lifting: Sei y
eine solche freie Variable in e, dann:
λx1 , . . . xn .e[y] → λz, x1 , . . . xn .e[z/y]) y
Fall: ein innerer Ausdruck ist ein µ-Ausdruck.
• Wenn t ≡ µx.e und e keine freien Variablen außer x enthält,
dann erzeuge eine neue Superkombinatordefinition: neuerkomb =
e[neuerkomb/x]. und ersetze den µ-Ausdruck in t durch neuerkomb.
• Wenn t ≡ µx.e und µx.e genau die genau freien Variablen
y1 , . . . , yn enthält, dann erzeuge eine neue Superkombinatordefinition: neuerkomb y1 . . . yn = e[(neuerkomb y1 . . . yn )/x]. und ersetze den
µ-Ausdruck in t durch (neuerkomb y1 . . . yn ). Der Gesamtausdruck e
wird als Definition main = e in die Menge der Definition aufgenommen
8.1.2
PCFK → PCF
Das Ziel dieser Übersetzung ist es, Namen durch Ausdrücke zu ersetzen.
Betrachte alle Definitionen als großes letrec.
letrec
name1 = λx1,1 , . . . , x1,m1 .d1 ,
...,
namen = λxn,1 , . . . , xn,mn .dn
in main
Die Kombinatoren lassen sich jetzt durch den n-dimensionalen Satz von
Bekić als Fixpunktausdrücke darstellen und damit hat man einen einzigen
Ausdruck, der mit λ und µ-Ausdrücken in PCF dargestellt werden kann.
Bemerkung 8.1 Für Funktionstypen gilt eine zum Verhalten von Ausdrücken vom Basistyp analoge Aussage:
Zu jedem Ausdruck e : τ1 → τ2 → . . . τn → num existiert ein übersetztes Programm und ein Ausdruck maine , so dass für alle Ausdrücke ei : τi und deren
Übersetzung gilt: e e1 . . . en hat Normalform m gdw. maine maine,1 . . . maine,n
die Normalform m hat.
Beispiel 8.2
1. PCF → PCFK :
e = µx : num.(µy : num.if zero? x then y else x ) ergibt:
TIDS 2, SS13, Kapitel 1, vom 11.4.2013
35
f x =
if zero? x then f x else x)
g = f g
2. PCFK → PCF: Maximumsfunktion:
max x y = if zero? x then y
else if zero? y then y else succ (max (pred x) (pred y))
max = µm. if zero? x then y
else if zero? y then y else succ(m(pred x)(pred y))
Bemerkung 8.3 Eigenschaften der Übersetzung: Für Ausdrücke vom Basistyp num gilt, dass die Ausdrücke vor und nach der Übersetzung jeweils zu
demselben Wert reduziert und auch das gleiche Terminierungsverhalten haben. D.h. Für Basistypen ist die Übersetzung operational gleich, die Anzahl
der Reduktionsschritte ist gleich bis auf konstante Faktoren, die im wesentlichen die evtl. unterschiedliche Zählweise bei Superkombinatorreduktion und
β- bzw. µ-Reduktion ausdrücken.
Für allgemeinere Typen gilt das nicht: Die Anzahl der Schritte ist etwas
weniger korreliert. Z.B. λxλy.x wird übersetzt in K x y = x.
Aber: der Ausdruck (K 1) ist in WHNF in PCFK , während (λxλy.x) 1
in einem Schritt zu λy.1 reduziert. Die Anzahl der zusätzlichen Reduktionsschritte ist aber begrenzt durch die Größe des Programms und des Ausdrucks.
Folgerung:
Verschränkte Rekursion entspricht der Anwendung des µ-Operator auf
Ausdrücken mit freien Variablen.
Herunterladen