PCF und denotationale Semantik

Ähnliche Dokumente
Der λ-kalkül. Frank Huch. Sommersemester 2015

WS 2009/10. Diskrete Strukturen

Haskell, Typen, und Typberechnung. Grundlagen der Programmierung 3 A. Einige andere Programmiersprachen. Typisierung in Haskell

Entwicklung eines korrekten Übersetzers

18 Höhere Ableitungen und Taylorformel

Was bisher geschah. deklarative Programmierung. funktionale Programmierung (Haskell):

Analysis I - Stetige Funktionen

(Man sagt dafür auch, dass die Teilmenge U bezüglich der Gruppenoperationen abgeschlossen sein muss.)

2 Mengen und Abbildungen

Kapitel VI. Euklidische Geometrie

Zahlen und metrische Räume

Lineare Algebra 1. Detlev W. Hoffmann. WS 2013/14, TU Dortmund

Statt (r s) schreiben wir in Zukunft meistens rs, gelegentlich auch (r; s).

Vorlesung. Funktionen/Abbildungen

Satz 16 (Multiplikationssatz)

00. Einiges zum Vektorraum R n

Halbgruppen, Gruppen, Ringe

Mathematische Grundlagen der Computerlinguistik

2. Universelle Algebra

Einführung in die Informatik 2

Ordnungsrelationen. Bernhard Ganter. Institut für Algebra TU Dresden D Dresden

Mengen und Abbildungen

5.4 Die Prädikatenlogik 1.Stufe als Semantikformalismus

Brückenkurs Mathematik

5 Zwei spieltheoretische Aspekte

Seminarvortrag aus Reiner Mathematik Existenz von Primitivwurzeln

Vorlesung. Einführung in die mathematische Sprache und naive Mengenlehre

Mengenlehre gibt es seit den achtziger Jahren des 19. Jahrhunderts. Sie wurde von

Logik, Mengen und Abbildungen

Kapitel III. Stetige Funktionen. 14 Stetigkeit und Rechenregeln für stetige Funktionen. 15 Hauptsätze über stetige Funktionen

Logic in a Nutshell. Christian Liguda

3.5 Ringe und Körper. Diese Eigenschaften kann man nun auch. 1. (R, +) ist eine kommutative Gruppe. 2. Es gilt das Assoziativgesetz bezüglich.

3 Vom Zählen zur Induktion

Algorithmen mit konstantem Platzbedarf: Die Klasse REG

Mathematische Grundlagen der Computerlinguistik Relationen und Funktionen

Grundlagen der Mengenlehre

Surjektive, injektive und bijektive Funktionen.

Stetigkeit von Funktionen

3. Relationen Erläuterungen und Schreibweisen

Hilbert-Kalkül (Einführung)

Caputo fraktionale Differentialgleichungen. 1 Riemann Liouville fraktionale Differentialgleichungen

Lineare Gleichungssysteme

1 Mengen. 1.1 Elementare Definitionen. Einige mathematische Konzepte

4.1 Grundlegende Konstruktionen Stetigkeit von Funktionen Eigenschaften stetiger Funktionen... 92

Konjunktive und disjunktive Normalformen

6. Induktives Beweisen - Themenübersicht

Kapitel 3. Natürliche Zahlen und vollständige Induktion

Aussagenlogik zu wenig ausdrucksstark für die meisten Anwendungen. notwendig: Existenz- und Allaussagen

Induktive Definitionen

Flüsse, Fixpunkte, Stabilität

Formale Sprachen. Spezialgebiet für Komplexe Systeme. Yimin Ge. 5ahdvn. 1 Grundlagen 1. 2 Formale Grammatiken 4. 3 Endliche Automaten 5.

Einführung in die Informatik 1

Lösungen zum Aufgabenblatt Nr. 1: Konstruktion der reellen Zahlen

Stetige Funktionen, Binomischer Lehrsatz

x y f : R 2 R 3, Es gilt: Bild f = y : wobei x,y R Kern f = 0 (wird auf der nächsten Folie besprochen)

Sudoku. Warum 6? Warum 6?

Grundkurs Semantik. Sitzung 3: Mengenlehre. Andrew Murphy

Kapitel 5: Applikative Programmierung

1. Man schreibe die folgenden Aussagen jeweils in einen normalen Satz um. Zum Beispiel kann man die Aussage:

Theoretische Grundlagen des Software Engineering

Kontextuelle Gleichheit, Korrektheit, Programmtransformationen, Auswertungsänderung

1 Bedingungen und der Typ bool. Informatik I: Einführung in die Programmierung 5. Bedingungen, bedingte Ausführung und Schleifen. Vergleichsoperatoren

Iterative Verfahren, Splittingmethoden

1. Einleitung wichtige Begriffe

Teil 7. Grundlagen Logik

5.1 Determinanten der Ordnung 2 und 3. a 11 a 12 a 21 a 22. det(a) =a 11 a 22 a 12 a 21. a 11 a 21

Motivation. Formale Grundlagen der Informatik 1 Kapitel 19. Syntax & Semantik. Motivation - Beispiel. Motivation - Beispiel

Syntax von LOOP-Programmen

Automaten, Spiele, und Logik

1.5 Abstrakte Interpretation, insbesondere zur Striktheitsanalyse

ε δ Definition der Stetigkeit.

Absolute Stetigkeit von Maßen

Technische Universität München. Lösung Montag WS 2013/14. (Einheitskreis, ohne Rechnung ersichtlich) (Einheitskreis, ohne Rechnung ersichtlich)

Kapitel 1.3. Normalformen aussagenlogischer Formeln und die Darstellbarkeit Boolescher Funktionen durch aussagenlogische Formeln

Programmierung 1 - Repetitorium

Kapitel III. Lineare Abbildungen

Wir starten mit der Entwicklung einer algebraischen Struktur, welche u.a. gut zur Kennzeichnung von Geometrien geeignet ist.

9.2 Invertierbare Matrizen

Grundbegriffe der Wahrscheinlichkeitstheorie

Gleichmäßige Konvergenz und Funktionenräume

Thema14 Der Satz über inverse Funktionen und der Satz über implizite Funktionen

Vorlesung Diskrete Strukturen Graphen: Wieviele Bäume?

Übung zu Grundbegriffe der Informatik. Simon Wacker. 15. November 2013

9.2. DER SATZ ÜBER IMPLIZITE FUNKTIONEN 83

Klausur Formale Systeme Fakultät für Informatik 2. Klausur zum WS 2010/2011

Thema 4 Limiten und Stetigkeit von Funktionen

Topologische Räume und stetige Abbildungen Teil 2

Übungen zur Vorlesung MATHEMATIK II

Übungen zur Vorlesung Mathematik für Informatiker 1 Wintersemester 2013/14 Übungsblatt 8

2 Euklidische Vektorräume

Funktionale Programmierung

WS 2008/09. Diskrete Strukturen

Kapitel 7: Formaler Datenbankentwurf

Lösungen zu Aufgabenblatt 7P

Funktionsgrenzwerte, Stetigkeit

Mathematischen Grundlagen und Notationen

Stetige Funktionen. Definition. Seien (X, d) und (Y, ϱ) metrische Räume und f : X Y eine Abbildung. D(f) X sei der Definitionsbereich von f.

Programmieren in Haskell

Verträge für die funktionale Programmierung Design und Implementierung

ÜBUNGSBLATT 11 LÖSUNGEN MAT121/MAT131 ANALYSIS II FRÜHJAHRSSEMESTER 2011 PROF. DR. CAMILLO DE LELLIS

Transkript:

Kapitel 3 PCF und denotationale Semantik In diesem Kapitel soll ein Beispiel für eine denotationale Semantik vorgestellt und eingehend untersucht werden, die auf eine funktionale Programmiersprache mit Funktionen höherer Ordnung anwendbar ist. Auf der einen Seite ist das eine einfach getypte funktionale Programmiersprache, PCF, die als Modell dient und in der man eine hinreichend interessante Ausdrucksstärke hat. Auf der Seite des Bereichs (domains), in den man abbildet, muss auch einiges an typischer Arbeit geleistet werden: Der Domain muss konstruiert werden. Wichtige Eigenschaften des Domains sind die Ordnungsrelation und Stetigkeit, die im wesentlichen die Berechnungskraft (Informationsgewinn) und die Terminierung von Berechnungen (von Ausdrücken) modellieren. Man kann in der Bereichstheorie noch weiter gehen und auch für polymorph getypte, und auch nicht-deterministische Programmiersprachen passende Bereiche konstruieren. Eine andere Richtung, die man verfolgen kann, ist die Untersuchung einer operationalen Variante der Semantik: die kontextuelle Semantik. 3.1 PCF: Programming Language for Computable Functions PCF ist eine einfach getypte funktionale Sprache, für die wir mit unseren Mitteln eine denotationale Semantik mittels Konstruktionen über cpo s (complete partial order) angeben können. PCF hat als Basisdatentypen natürliche Zahlen und die beiden Wahrheitswerte True, False. Einige wenige Funktionen sind schon vorhanden. 1

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 2 3.1.1 Syntax Typen: Ausdrücke: τ ::= num Bool τ τ 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 Zur Vermeidung eines mehrdeutigen Parse kann man in der Sprachnotation Klammern benutzen. Alle zulässigen Ausdrücke sind einfach (monomorph) getypt. D.h. 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, der der Einführung der Rekursion entspricht. Typregel für µ ist: λx.e : τ τ (µx :: τ.e) : τ Beachte, dass es auch PCF-Definitionen gibt, bei denen der Zahlbereich nur mit succ als Konstruktor und 0 als Konstante aufgebaut wird. 3.2 Operationale Semantik von PCF Die operationale Semantik wird zunächst als Big-Step Semantik angegeben. Mit v, w bezeichnen wir Werte (d.h. Zahlen, True, False) oder einen λ- oder µ-ausdruck. Die Regeln für die Reduktion op sind:

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 3 e 1 op True; e 2 op v if e 1 then e 2 else e 3 op v s op (λx.e) e[a/x] op w (λx.e) a op w t op pred; e op n und n > 0 t e op (n 1) e 1 op False; e 3 op v if e 1 then e 2 else e 3 op v (e[(µx.e)/x]) op w (µx.e) op w 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 0 t e op False Als Reduktionsrelation (d.h. Small-Step Semantik ) kann man das auch folgendermaßen definieren: if True then e 2 else e 3 e 2 if False then e 2 else e 3 e 3 pred n n 1 falls n > 0 pred 0 0 succ n n + 1 zero? 0 True zero? n False falls n > 0 (λx.e)a e[a/x] (µx.e) (e[µx.e/x]) Wir brauchen noch Reduktionskontexte in PCF: R ::= [] [] E if [] then E else E pred [] succ [] zero? [], und erlauben nur Reduktionen, die in einem Reduktionskontext stattfinden: d.h. es soll gelten: s t R[s] R[t] 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.2.1 Sei t ein PCF-Ausdruck. t ist in Normalform gdw. t nicht mehr reduzierbar ist. t ist in WHNF (schwache Kopfnormalform): gdw. t λx.t oder t ist ein Basiswert vom Typ num oder Bool.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 4 3.2.1 Ü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 auch 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 es in PCF nicht gibt. 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: t H H n H. Ü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 E H (µx : τ.e) H = fix(\x E H ) Folgende Haskell-Definition werden für diese Übersetzung benötigt: pred n = if n > 0 then n-1 else 0 succ n = n+1 iszero x = x == 0 fix f = f (fix f) Man kann alternativ auch das let(rec)-konstrukt zur Übersetzung des µ verwenden: (µx : τ.e) H = let x = E H in x Die Fixpunktbildung entspricht offenbar der (einfachen) Rekursion. Beispiel 3.2.2 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:

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 5 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 = (\s t -> if iszero s then t else succ (a (pred s) t)) in a Beispiel 3.2.3 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 3.3 Eigenschaften von PCF Definition 3.3.1 Eine Relation ist konfluent (bzw. Church-Rosser) gdw. für alle s, s 1, s 2 : wenn s s 1 und s s 2, dann existiert ein weiterer Term s 3, so dass s 1 s3 und s 2 s3. Satz 3.3.2 Die Reduktionsrelation ist konfluent. Beweis. Mit Methoden wie im Buch von Barendregt. Ohne den Fixpunktoperator ist die Sprache PCF 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. Lemma 3.3.3 In PCF gilt: geschlossene Terme, die (WHNFs oder 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: Wir gehen die Konstruktionsmöglichkeiten durch.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 6 M 0, n, True, False: trivial. M pred M, succ M, zero? M : Das gilt mit Induktion, da M ebenfalls von Basistyp, und eine Ein-Schritt Reduktion ausreicht, um den Ausdruck ebenfalls in einen Basiswert zu überführen. M if M 0 then M 1 else M 2 : wenn dieser Term in Normalform (bzw. WHNF) ist, dann auch M 0, also ist M 0 der Basiswert True oder False. Dann kann man aber reduzieren. M λx.m : kann nicht sein, da dies vom falschen Typ ist. M µ.m : kann nicht sein, da dieser Term reduzierbar ist. M (M 1 M 2 ): Dieser Term muß von der Form ((... (M n M n 1 ).....)M 2 ) sein. Der Term M n 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. predund succsind bereits behandelt. Ein µ-ausdruck kann es ebenfalls nicht sein, da der reduzierbar wäre. Damit kann man aber β-reduzieren, und der Term ist nicht in Normalform. Wir betrachten jetzt das Fragment P CF µ : das ist die Sprache PCF ohne den µ-operator. Diese Sprache ist vergleichbar mit dem einfach getypten Lambdakalkül. Definition 3.3.4 Definiere eine Menge T trm 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 T trm. 2. Sei M : τ 1 τ 2. Dann ist M T trm, wenn für alle geschlossenen N : τ 1 T trm gilt: (M N) T trm. Jetzt definiere noch die Menge T trm,v folgendermaßen: Wenn M die freien Variablen x 1,..., x n hat, dann ist M T trm,v, wenn σ(m) T trm für alle σ = {x i N i i = 1,..., n} wobei N i T trm. Offenbar gilt: 1. Sei M T trm. Dann terminieren alle von M ausgehenden Reduktionen. 2. M T trm,v gdw. für alle σ, die T trm - Terme für freie Variablen einsetzen und für alle geschlossenen N 1... N k T trm, so dass σ(m) N 1... N k von Basistyp ist : σ(m) N 1... N k hat nur terminierende Reduktionen. Satz 3.3.5 Betrachte das Fragment P CF µ der Sprache PCF. Für die Ausdrücke dieser Sprache gilt, dass sie in T trm,v enthalten sind. D.h. Man kann jeden geschlossenen Term in P CF µ nur endlich oft reduzieren.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 7 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, succ M, zero?(m ): Betrachte σ(m ). Nach Induktion terminiert σ(m ). Wegen der operationalen Definitionen von succ, pred, zero? gilt dies auch für σ(m). Also ist M T trm,v. M if M 0 then M 1 else M 2 Nehme an, dass N i T trm sind und σ Substitution, die nur Terme aus T trm einsetzt, so dass (σm) N 1... N n geschlossener Term vom Basistyp ist. Induktion zeigt, dass (σ M 0 ) terminierend ist, ebenso (σm 1 ) N 1... N n und (σm 2 )N 1... N n also ist auch (σm) N N 1... N n terminierend. Dies gilt für alle terminierenden, geschlossenen N i. Also gilt M T trm,v. M (L N): Da L, N T trm,v nach Induktionshypothese, gilt für alle Substitutionen σ die nur Terme aus T trm einsetzen, dass auch σl, σn T trm. Wegen der Definition von T trm ist auch (σl (σn)) terminierend, also ist (L N) T trm,v. M = λx.m : Zu zeigen ist: (λx.σm )N T trm ist terminierend für geschlossene N T trm,v und Substitutionen σ, die nur Terme aus T trm einsetzen. Da M T trm,v nach Induktionshypothese, gilt s[n/x]m T trm. Also gilt das auch für σ(m [N/x]). Damit ist M = λx.m T trm,v. Korollar 3.3.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 3.3.7 Der µ-operator ist nicht im µ-freien PCF-Fragment definierbar. D.h dies ist eine echte Erweiterung. 3.4 Vollständige partielle Ordnungen, stetige Funktionen Die Betrachtung vollständiger partieller Ordnungen soll eine einfache und verständliche Einführung in einen Ansatz zur denotationalen Semantik liefern. Die Sprache, der wir eine denotationale Semantik geben werden, ist PCF, eine Variante einer einfach getypten funktionalen Programmiersprache.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 8 Polymorph getypte funktionale Programmiersprachen und deren denotationale Semantik würden den Rahmen dieser Vorlesung sprengen. Definition 3.4.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 3.4.2 Sei X P, wobei P eine partiell geordnete Menge ist. Sei p P. Wenn x X : x p dann ist p obere Schranke von X. p P ist eine kleinste obere Schranke (lub, least upper bound) von X, wenn p obere Schranke von X und für jede andere obere Schranke q von X gilt p q. Bezeichnung: p = X. Analog kann man untere Schranken, glb usw. definieren Definition 3.4.3 Eine Folge a 1 a 2 a 3... mit Elementen a i P heißt Kette (ω-kette). Die partielle Ordnung auf P ist vollständig, wenn jede aufsteigende Kette a 1 a 2 a 3... eine kleinste obere Schranke hat. Bezeichnung: i a i. Wenn die partielle Ordnung P ein kleinstes Element hat, dann sagt man, ist eine partielle Ordnung mit kleinstem Element. Als Abkürzung sprechen wir bei einer vollständig partiellen Ordnung mit kleinstem Element von einer cpo (complete partial order) 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 3.4.4 Die Potenzmenge einer Menge mit ist eine cpo mit kleinstem Element. Für eine aufsteigende Kette M 1 M 2 M 3... gilt i M i = i M i. 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.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 9 Definition 3.4.5 Monotonie Eine Funktion f : D E, wobei D, E cpo s sind, ist monoton, gdw. für alle d 1 D d 2 gilt, dass auch f(d 1 ) E f(d 2 ). Stetigkeit Die Funktion f : D E ist stetig, wenn sie monoton ist und wenn für alle Ketten d 1 D d 2 D d 3 D... gilt, dass f( i d i) = i f(d i). Bemerkung 3.4.6 Die Intuition 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, d.h. man hat mehr Funktionen bereitgestellt, als die Programmiersprache ausdrücken kann. Im folgenden bezeichnen wir für eine Funktion f : D D. die mehrfache Anwendung f(... f(d) auch als f n (d). }{{} n Beispiel 3.4.7 Alle Funktionen, die auf einer diskreten cpo definiert sind, sind stetig. 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 3.4.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. 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. Definition 3.4.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).

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 10 Satz 3.4.10 (Fixpunktsatz) Sei f : D D eine stetige Funktion, wobei D eine cpo mit kleinstem Element ist. Dann hat 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 fix(f). Beweis. Da D eine cpo ist, existiert f n ( )). Es gilt: f( f n ( )) = f n+1 ( )) (Stetigkeit von f und da f( )...f n ( )... aufsteigende Kette ist) = f n ( )) da f( ) und es bis auf die gleiche Kette ist. Damit haben wir gezeigt, dass f n ( )) 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 monoton ist. Mit Induktion nach n erhält man: f n ( ) d, also auch f n ( )) d, da f n ( )) kleinste obere Schranke der aufsteigenden Kette f( )...f n ( )... ist. Dieselbe Argumentation zeigt auch dass f n ( )) kleinster Präfixpunkt von f ist. Es gilt folgender allgemeinerer Satz (siehe z.b. Davey und Priestley) Satz 3.4.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 ( ) konstruierbar, auch wenn es ein kleinstes Element gibt. Beispiel 3.4.12 Für eine nicht-monotone Funktion. Gegeben sei die Boolesche cpo: {True, False, } mit True, False. Die Funktion, die Terminierung testet ist: f : False, True True, False True. Diese Funktion f ist nicht monoton. Denn: True, aber f = False True = f True. Beispiel 3.4.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 $ das Ende markiert und sonst nicht auftauchen 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.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 11 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. 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 hateins mit hateins (0 ω ) = False wäre nicht mehr stetig, da False nicht der lub der approximierenden Folge ist. 3.4.1 Zusammenhang zwischen partiellen und totalen Funktionen bei IMP Es folgen einige Bemerkungen und Motivationen zur Einführung von und totalen Funktionen auf einer Ordnung, wobei wir hier an die Sprache IMP und deren Semanti erinnern. Es gibt einen einfachen, allgemeinen Zusammenhang zwischen partiellen Funktionen Σ Σ 1 und totalen Funktionen auf Σ Σ, wobei Σ = Σ { } 1 Σ war definiert als Menge der Zustände, d.h. der möglichen Speicherbelegungen

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 12 ist, wobei als das kleinste Element von Σ definiert ist. Σ selbst ist diskret geordnet, aber ist so definiert, dass es kleiner ist als alle anderen Elemente. Eine partielle Funktion f : Σ Σ wird abgebildet auf eine totale Funktion f : Σ Σ, so dass { f wenn f auf a undefiniert ist (a) = f(a) sonst Die Ordnung f g auf partiellen Funktionen (d.h. Teilmengenrelation auf Funktionsgraphen), passt zusammen mit der Ordnung der zugehörigen Funktionen f, g : Σ Σ, die punktweise definiert ist: { f g f gdw. a (a) = g (a), wenn beide definiert sind f (a) g (a) sonst Offenbar gilt: f g gdw. f g. Die Denotation einer while-schleife kann man dann auch als totale Funktion in Σ Σ repräsentieren. Die Ordnung entspricht dem Informationsgehalt: Kleinere Funktionen berechnen weniger Information. Die Monotonie einer Funktion bedeutet: wenn man Information über die Argumente einer Funktion hat, dann bleibt diese erhalten nach Anwendung einer Funktion: wenn die Argumente mehr Information liefern, dann haben auch die Werte nach Anwendung mehr Information. Im Fall von IMP heißt das, dass die Definiertheit einer Funktion bleibt erhalten. Die Stetigkeit einer Funktion bedeutet, dass man Information nur gewinnen darf aufgrund von Auswertungen, evtl. auch abgebrochenen Auswertungen, aber nicht durch Meta-Schließen über das Programm, o.ä. 3.4.2 Vollständige Verbände, Knaster-Tarski Fixpunktsatz Wir erwähnen noch den Fixpunktsatz für vollständige Verbände. Definition 3.4.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 M ist immer ein vollständiger Verband bzgl. lub und glb sind durch {M i } bzw. durch {M i } definiert. In einem Verband genügt die Monotonie einer Funktion zur Existenz eines Fixpunktes. Der ist auch einfach zu konstruieren.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 13 Satz 3.4.15 (Knaster, Tarski) Sei D ein Verband und Φ : D D eine monotone Abbildung. Dann existiert ein kleinster und größter Fixpunkt von Φ. Der kleinste Fixpunkt ist {x Φ(x) x}. Der größte Fixpunkt ist {x Φ(x) x}. 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 wegen der Montonie von Φ auch Φ(Φ(α)) Φ(α) und damit nach Definition von H auch Φ(α) H. D.h. aber, α = H Φ(α) und somit folgt aus der Antisymmetrie: α = Φ(α). D.h. α ist ein Fixpunkt. Da alle Fixpunkte β per Definition in H sind, muß α kleinster Fixpunkt von Φ sein. 3.4.3 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 3.4.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 = Id E und g f = Id D ist. D und E heißen dann isomorph. I. a. unterscheiden wir isomorphe cpo s nicht, da sie im wesentlichen dasselbe Objekt sind. Aussage 3.4.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 monoton ist. Wir zeigen die Stetigkeit von f: Sei a 1 D a 2 D... eine aufsteigende Kette und sei a die kleinste obere Schranke zu a i. Dann existiert zu f(a 1 ) E f(a 2 ) E... eine kleinste obere Schranke e. Da f(a i ) E f(a) für alle i, gilt e f(a). Wegen der Voraussetzung ist a i g(e) für alle i, d.h. g(e) ist obere Schranke der a i mit g(e) a. Da a der lub 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.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 14 3.4.4 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. Aussage 3.4.18 Jede Funktion f : D E ist stetig, wenn D eine diskrete cpo ist. 3.4.5 Endliche Produkte. Seien D i, i = 1,..., k cpo s. Wir können immer annehmen, dass die Mengen disjunkt sind. D 1... D k ist das zugehörige cartesische Produkt (die Menge der k-tupel). Die Ordnung darauf ist punktweise definiert: (d 1,..., d k ) (d 1,..., d k) gdw. d i d i für alle i. Es gilt für Ketten von Tupeln (d 1j,..., d kj ), dass j (d 1j,..., d kj ) = ( j d 1j,..., j d kj). (Übungsaufgabe) Die Projektionsfunktionen π i sind definiert durch π i (d 1,..., d k ) = d i. Diese Projektionsfunktionen sind stetig. Tupelbildung kann man für Funktionen ebenfalls verwenden: Seien f i : E D i, i = 1,..., k stetige Funktionen auf den cpo s E, D i. Dann ist die Funktion f 1,..., f k : E (D 1,..., D k ) definiert durch f 1,..., f k e := (f 1 (e),..., f k (e)). Die Funktion f 1,..., f k erfüllt π i f 1,..., f k = f i. Die Funktion f 1,..., f k ist offenbar monoton. Sie ist auch stetig: Sei e 0 e 1 e 2... eine Kette in E. f 1,..., f k ( i e i) = (f 1 ( i e i),..., f k ( i e i)) = ( i (f 1(e i ),..., i f k(e i )) da alle f i stetig sind = i ((f 1(e i ),..., f k (e i )) (Definition des Produktes) = i f 1,..., f k (e i ) Definition von... Die Produktbildung kann man für Funktionen und Kreuzprodukt von cpo s gemeinsam machen: Seien f i : D i E i stetige Funktionen auf cpo s. Dann ist f 1... f k : D 1... D k E 1... E k definiert durch f 1... f k (d 1,..., d k ) = (f 1 (d 1 ),..., f k (d k )). Dies kann man schreiben als f 1... f k = f 1 π 1,..., f k π k. Daraus kann man schließen, dass diese Funktion ebenfalls stetig ist.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 15 Lemma 3.4.19 Sei h : E D 1... D k 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)) = (π 1 h,..., π k h (x). Diese Funktion ist stetig, da π i h stetig ist, und... stetige Funktionen erzeugt. Wir zeigen auch eine analoge Aussage für eine Funktion h : D 1... D k E. Diese Aussage ist etwas schwerer zu zeigen. Dazu benötigen wir ein Lemma über lubs von aufsteigenden arrays. Lemma 3.4.20 Sei e i,j Elemente einer cpo E, so dass e i,j e i,j wenn i i und j j. Dann gilt: {e i,j i, j IN} hat eine kleinste obere Schranke und e i,j = ( e i,j ) = ( e i,j ) = i,j i j j i i Beweis. Es ist eine leichte Übung, zu zeigen, dass die Mengen {e i,j }, {e i,i }, { j e i,j}, { i e i,j} die gleichen oberen Schranken haben. Zunächst gilt, dass e i,j e k,k wenn k = max{i, j}. Sei a eine obere Schranke von { j e i,j}. Dann ist a auch obere Schranke von {e i,j }. Umgekehrt sei a obere Schranke von {e i,j }. Dann ist für jedes i: j e i,j a, denn das linke ist eine kleinste obere Schranke einer Teilmenge. Damit ist a auch obere Schranke von { j e i,j}. e i,i Lemma 3.4.21 Sei f : D 1... D k E eine Funktion auf cpo s D i, E. Dann ist f stetig gdw. f stetig in jedem Argument ist. Beweis. : Sei f stetig: Für feste d j definiere die Funktion f i (x) := f(d 1,..., d i 1, x, d i+1,..., d k ). Diese ist stetig. : Sei der Einfachheit halber k = 2 und sei (x 0, y 0 ) (x 1, y 1 )... eine aufsteigende Kette. Dann gilt: f( i (x i, y i )) = f( i x i, j y j) lubs für kartesisches Produkt = i f(x i, j y j) da f stetig im ersten Argument = i ( j f(x i, y j ) da f stetig im zweiten Argument = i f(x i, y i ) wegen obigem Lemma 3.4.6 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).

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 16 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 f 0 f 1 f 2... Für d D können wir f 0 (d) f 1 (d)... bilden. Deshalb existiert i f i(d). D.h die Funktion i f i ist so definiert, dass ( i f i)(d) := i f i(d). Es bleibt zu zeigen, dass ( i f i) stetig ist, d.h. [D E] ist. Sei d 0 d 1 d 2... eine Kette in D. Dann gilt: ( i f i)( j d j) = ( i f i( j d j) nach Definition der Funktion( i f i) = i j f i(d j ) da f i stetig ist = j i f i(d j ) nach dem Lemma über aufsteigende arrays = j (( i f i)(d j )) Definition von lub für Funktionen Beispiel 3.4.22 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. 3.4.7 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 f 0 f 1 f 2 eine Kette von Funktionen. Dann gilt: apply(( i f i), d) = ( i f i)d = i f i(d) (Definition des lubs von Funktionen) = i apply(f i, d) 2. Stetig im zweiten Argument: Sei d 0 d 1 d 2... eine Kette. apply(f, ( i d i)) = f( i d i) = i f(d i) da f stetig = i apply(f, d i) Beispiel 3.4.23 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:

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 17 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. 2. curry(g) ist stetig für alle g: v 0 v 1 v 2... sei Kette in F. Sei d D beliebig. Dann betrachte curry(g)( i v i)d = g(( i v i), d) = i g(v i, d) = = i curry(g) v i d Also stimmt die Funktion curry(g)( i v i) mit ( i curry(g) v i ) überein. D.h. curry(g) ist stetig. 3. curryist stetig: Sei g 1 g 2 g 3... Dann gilt: i (curry(g i )) d e = i g i(d, e) = i g i(d, e) = ( i g i)(d, e) da g i [F D E] = curry( i g i) d e D.h. i curry(g i) und curry( i g i) stimmen als Funktionen überein. Damit ist curry stetig. 3.4.8 Lifting Diese Konstruktion addiert ein neues kleinstes Element (Bottom-Element) zu einer cpo. Der Zweck ist i.a., partiell definierte Funktionen handhabbarer zu machen, insbesondere diese auf bekannte totale Funktionen zurückzuführen. Eine wichtige Anwendung ist die Repräsentation einer mathematisch gegebenen Berechnungsmöglichkeit durch eine algorithmische, wobei undefiniert und Nichtterminierung mitbehandelt werden sollen. Die Konstruktion selbst ist einfach: Definition 3.4.24 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 3.4.25 Die Einbettung ist stetig. Definition 3.4.26 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 f(d) wenn d (d) = sonst. d.h. d = E

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 18 Aussage 3.4.27 f ist stetig. Sei d 1 d 2 : wenn d 2 =, dann ist d 1 =. wenn d 2, dann ist f (d 1 ) = f(d 1 ) oder f (d 1 ) = E f(d 2 ). Sei d 1 d 2... eine aufsteigende Kette. Wenn ein d i, dann ist lub f (d i ) = lub f(d i ) = f(lub d i ) = f (lub d i ) da (lub d i ). Wenn d i =, dann ist lub f (d i ) = lub = = f (lub d i ). Bemerkung 3.4.28 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 (λx.e d) übersetzt werden. Lemma 3.4.29 Die (Lifting-)Funktion (.) : [D E] [D E] ist stetig: Beweis. Sei f g. Sei d D. Wenn d =, dann ist f = = g. Wenn d, dann betrachte f d g d. Wegen f (d) = f(d) und g (d) = g(d) ist (.) monoton. Sei f 0 f 1... eine Kette in [D E] und d ein Element aus D. Wenn d =, dann ist ( i f i) =. Die Kette i f i ist konstant =, also ebenfalls =. Sei d. Dann gilt: ( i f i) d = i f i)d (denn so war (.) definiert) = i f i(d) = i f i (d) Stetigkeit von f i und Definition von (.) = ( i f i )(d) Da ( i f i ) punktweise definiert ist Mathematische Operationen auf Mengen können damit geliftet werden. Zum Beispiel (In PCF auch) 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.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 19 Für die Untersuchung der Äquivalenzen im ungetypten (lazy) Lambda-Kalkü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 3.4.30 Eine Funktion f : D E, wobei D, E cpo s mit sind, ist strikt gdw f = 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 (strict f) d = wenn d = E 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. 3.4.9 Summen von cpo s; Fallunterscheidungen Die Summen-Konstruktion ist die Grundlage der Semantik des case- Konstruktes. Die gibt es in PCF so nicht. Damit könnte man dann eine Semantik für PCF mit Konstruktoren und case konstruieren. Definition 3.4.31 Seien D 1,..., D k (disjunkte) cpo s. Die Summe dieser cpo s ist einfach die (disjunkte) Vereinigung D i, wobei die Ordnung genau übertragen wird. Die Elemente aus verschiedenen cpo s sind unvergleichbar. Die Summe schreiben wir auch als D 1 +... + D k. Es gibt (injektive) Einbettungen ι i : D i D 1 +... + D k mit ι i (d i ) = d i Es gilt: Alle Einbettungen sind stetig. (offensichtlich) Oft wird diese Summe explizit mit Konstruktoren konstruiert, da die D i s möglicherweise nicht disjunkt sind: Wenn man c i als verschiedene Konstruktoren hat, dann definiert man D 1 +... + D k = {c i (d i ) i = 1,..., k}. Man kann stetige Funktionen in eine gemeinsame cpo E auch summieren : Sei f i : D i E stetige Funktionen auf cpo s. Dann definiert man [f 1,..., f k ] : D 1 +... + D k E folgendermaßen: [f 1,..., f k ]ι i (d i ) = f i (d i ). D.h. [f 1,..., f k ] ι i = f i.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 20 Hat man die Summe mit Konstruktoren c i definiert, dann definiert man: [f 1,..., f k ]c i (d i ) = f i (d i ). Es gilt: Die Operation [.,...,.] : ([D 1 E]... [D k E]) ((D 1 +... + D k ) 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. Seien zwei Funktionen gegeben: λx 1.e 1 : True E λx 2.e 2 : False E Dann kann man definieren: cond 0 (t, e 1, e 2 ) := [λx 1.e 1, λx 2.e 2 ](t) Wenn t = True ergibt, dann erhält man (λx 1.e 1 )(True), im anderen Fall (λx 2.e 2 )(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, e 1, e 2 ) := (slet t = b in cond 0 (t, e 1, e 2 )) Dies ist das cond mit folgendem Verhalten: wenn t =, dann ist cond(t, e 1, e 2 ) =, wenn t = True, dann e 1 (True) wenn t = False, dann e 2 (False) D.h., Die Summenkonstruktion gehört zu einem if. Das kann man ohne Probleme zu einem case verallgemeinern: D 1 +... + D k sei Summe von cpo s, die mit Konstruktoren c i konstruiert worden ist. Wenn Funktionen λx i.e i : D i E gegeben sind, dann entspricht [λx 1.e 1,..., λx n.e n ](d) einer Konstruktion mit case: case d of (c 1 (x 1 ) e 1 (x 1 ));... (c n (x n ) e n (x n )) 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 (D 1 +... + D k ) als Summe von cpo s, die mit Konstruktoren c i konstruiert worden ist. Wenn Funktionen λx i.e i : D i E gegeben sind, dann entsprechen sich: (slet x = d in [λx 1.e 1,..., λx n.e n ](d)) und case d of (c 1 (x 1 ) e 1 (x 1 ));... c n (x n ) e n (x n );

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 21 3.4.10 Stetigkeit von fix Sei D eine cpo mit. Der kleinste-fixpunkt-operator fix : [[D D] D] ist definiert als fix f = f n ( ) n Dies kann man auch schreiben als fix = n λf.f 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 fix f = n f n ( ) = ( n λx.x n ( ))f Also gilt: fix [[D D] D]. 3.5 Eine Metasprache und Stetigkeit Die Idee einer Metasprache ist eine Möglichkeit, Funktionen auf cpos zu mittels einer Lambdasyntax zu definieren, und dann diese Funktionen auf cpos als stetig nachzuweisen. Der Vorteil ist die Allgemeinheit: man hat dann einen großen Satz an stetigen Funktionen, für deren Stetigkeit man nicht jedesmal einen expliziten Beweis führen muss. 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 cpos gibt, so dass wir auch die neu konstruierten cpo s verwenden dürfen. E ::= D i E 1... E n [E 1 E 2 ] [E 1,..., E n ] E 3.5.1 Syntax für die Lambda-Meta-Sprache über cpos Für Ausdrücke: c i Konstanten für (alle) Elemente in einer festen cpo. Konstruktoren c E für bestimme cpo s. Wir nehmen auch an, dass die Summen-cpo s aus cpo s gebildet werden, die mit Konstruktoren verkleidet sind.

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 22 Funktionen, die fest an eine bestimmte cpo s gebunden sind: Projektionen, Einbettungen Generische Konstanten, wie apply, curry, fix, strict. e ::= x λx.e (e 1 e 2 ) (slet x = e 1 in e 2 ) (case e of(c E,1 (x 1 ) e 1 );... ; (c E,n (x n ) e n )) 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. (e 1 e 2 ): e 1 : [D E], e 2 : D und (e 1 e 2 ) gehört zur cpo E (slet x = e 1 in e 2 ): x gehört zur cpo D, e 1 zur cpo D ; e 2 und (slet x = e 1 in e 2 ) gehören zur gleichen cpo E (case e of(c E,1 (x 1 ) e 1 );... ; c E,n (x n ) e n ): Der Ausdruck e gehört zur cpo E, die Variable x i gehört zur E i, c E,i ist der Konstruktor, der E i versteckt. E = [E 1,..., E n ]. 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. 3.5.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 angepasste Definition der Stetigkeit eines Ausdrucks mit freien Variablen. Definition 3.5.1

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 23 1. Sei e ein Ausdruck, und seien x 1,..., x n die freien Variablen in e. Sei x eine Variable. Dann ist e stetig in der Variablen x, wenn für alle zulässigen Belegungen x i : D i mit d i D i für x i x gilt, dass λx.e[d i /x i i = 1,..., n, x i x] stetig ist. 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 {x 1 : D 1,..., x n : D n } als Funktion von D 1... D n 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 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 (e 1,..., e n ): Wenn alle e i stetig in allen Variablen sind, dann ist (e 1,..., e n ) ebenfalls stetig in allen Variablen: Sei eine zulässige cpo-belegung für das Tupel gegeben. Diese ist auch eine für jedes e i. Da alle λx.e i [d y /y x y] stetig sind, gilt das auch für λx.(e 1,..., e n )[d y /y x y] (dies folgt aus den Betrachtungen über Tupelbildung von Funktionen Also ist (e 1,..., e n ) stetig in seinen Variablen Anwendung Sei e ein Ausdruck. Betrachte zunächst (apply(e 1, e 2 )) wobei apply eine generische Konstante ist. Dann gilt: wenn (e 1, e 2 ) stetig ist, dann auch (apply (e 1, e 2 )). Sei eine Belegung von (apply(e 1, e 2 )) gegeben. Dann ist dies auch eine gemeinsame Belegung von apply und (e 1, e 2 ). Sei außerdem (e 1, e 2 ) stetig in x. Die cpo von (e 1, e 2 ) wurde konstruiert als (D E) D. Dann ist λx.((e 1, e 2 )[d y /y y x]) stetig und in [F (D E) D]. Dann ist auch apply λx.(e 1, e 2 )[d y /y] stetig. Offenbar gilt: apply λx.(e 1, e 2 )[d y /y] = λx.apply(e 1, e 2 )[d y /y]. Denn (apply λx.(e 1, e 2 )[d y /y](a) = (apply(e 1, e 2 )[d y /y, a/x]

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 24 = (e 1, e 2 )[d y /y, a/x] = (λx.apply(e 1, e 2 )[d y /y])(a) Damit ist apply(e 1, e 2 ) stetig. Offenbar ist apply(e 1, e 2 ) = (e 1 e 2 ). Also ist Anwendung stetig. Abstraktion λz.e ist stetig in allen Variablen Dazu betrachte λx.λz.e[d y /y y 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[d y /y y x, z] als Funktion von D 1 D 2 E stetig, wobei x, z die Variablen sind mit x : D 1 und z : D 2. Dann können wir diese Funktion transformieren mit curry: curry(λ(x, z).e) = λx.λz.e[d y /y y x, z]. Da curry als stetig bereits nachgewiesen ist, ist damit die Funktion λx.λz.e[d y /y y x, z] stetig. Dies gilt für alle x, also ist λz.e stetig in seine Variablen slet: (slet x = e 1 in e 2 ): Bedingung an eine Belegung mit cpo s ist : e 1 : D, x : D, e 2 : E. e 2 ist stetig in allen Variablen, insbesondere ist (λx.e 2 ) stetig; damit auch die geliftete Funktion (λx.e 2 ). Dies haben wir bereits bei der Betrachtung zu Lifting gesehen. Da Anwendung ebenfalls eine Konstruktion ist, die zu stetigen Funktionen führt, gilt: (λx.e 2 ) (e 1 ) ist stetig in allen Variablen. Denn es gilt: (slet x = e 1 in e 2 ) = (λx.e 2 ) (e 1 ) case: (case e of (c E,1 (x 1 ) e 1 );... ; (c E,n (x n ) e n )) Hier muss die Belegung mit cpo s so sein, wie oben beschrieben: (e gehört zur cpo E, die Variable x i gehört zur cpo E i, c E,i ist der Konstruktor, der E i versteckt. E = [E 1,..., E n ]. Diese Konstruktion ist stetig, da (case e of (c E,1 (x 1 ) e 1 );... ; (c E,n (x n ) e n )) als [λx 1.e 1,..., λx n.e n ] e definiert ist. Damit sind auch alle syntaktischen Konstruktionen wie (if a then b else c ) stetig. 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. 3.6 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

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 25 eine Repr sentation der Umgebung. 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 Beachte, dass Funktionenräume automatisch ein haben, da alle Basistypen ein haben. Cartesische Produkte werden nicht benötigt, da es keine Datenkonstruktoren, insbesondere keine Tupel in PCF gibt. Die zugehörigen Denotationen für geschlossene Terme M :: τ sind dann jeweils aus der Denotation [τ ] des Typs τ, d.h. [M ] [τ ]. Bei offenen 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 3.6.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 Variablen 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 λ, d.h. hier verwenden wir die Metasprache über cpos. [x] ρ = ρ(x) [n] ρ = n [pred M ] ρ = ([M ] ρ) 1 wenn ([M ] ρ) > 0 = 0 wenn ([M ] ρ) = 0 = sonst [succ M ] ρ = ([M ] ρ) + 1 wenn ([M ] ρ) 0 = wenn ([M ] ρ) = [if M then L 1 else L 2 ] ρ = wenn [M ] ρ = = [L 1 ] ρ wenn [M ] ρ = True = [L 2 ] ρ wenn [M ] ρ = False [(M N)] ρ = ([M ] ρ) ([N ] ρ) [λx.m ] ρ = λy.([m ](ρ[x y])) [µx.m ] ρ = fix(λy.([m ](ρ[x y])) (hier müssen M und x gleichen Typ haben)

Funktionale Programmierung 2, WS 2003/4, cpo, 28. Oktober 2003 26 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 3.6.2 Seien M und N PCF-Ausdrücke. Wenn M [M ] ρ = [N ] ρ für alle ρ. N, dann gilt: 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 L 1 else L 2 ) L 1 : [if True then L 1 else L 2 ] ρ = [[L 1 ] ρ 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]))(fix(λy.([m ](ρ[x y]))) = (λy.([m ](ρ[x y])))([(µx.m)] ρ) = ([M ](ρ[x ([(µx.m)] ρ)]))) = [(M[(µx.M)/x])] ρ Aussage 3.6.3 Für alle passenden Typen gilt: [λx. ] = [ ].