Aspekte funktionaler Sprachen
Einführung in den λ-Kalkül André Metzner 02.02.01
Inhaltsverzeichnis
Inhaltsverzeichni...
9 downloads
307 Views
319KB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Aspekte funktionaler Sprachen
Einführung in den λ-Kalkül André Metzner 02.02.01
Inhaltsverzeichnis
Inhaltsverzeichnis
Inhaltsverzeichnis 1 Einleitung
3
2 Untypisierter λ-Kalkül 2.1 Syntax . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Currying . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Bedeutung der Konstanten . . . . . . . . . . . . . . 2.4 Freie und gebundene Variablen . . . . . . . . . . . 2.5 Syntaktische Äquivalenz . . . . . . . . . . . . . . . 2.5.1 De-Bruijn-Notation . . . . . . . . . . . . . . 2.5.2 α-Konversion . . . . . . . . . . . . . . . . . 2.5.3 Substitution . . . . . . . . . . . . . . . . . . 2.6 β-Reduktion . . . . . . . . . . . . . . . . . . . . . . 2.6.1 Definition der β-Reduktion . . . . . . . . . 2.6.2 Konfluenz der β-Reduktion . . . . . . . . . 2.6.3 β-Konversion . . . . . . . . . . . . . . . . . 2.7 η-Reduktion . . . . . . . . . . . . . . . . . . . . . . 2.8 Reduktionstrategien . . . . . . . . . . . . . . . . . 2.9 Aufrufmechanismen höherer Programmiersprachen 2.10 Rekursion . . . . . . . . . . . . . . . . . . . . . . . 2.11 Der reine λ-Kalkül . . . . . . . . . . . . . . . . . . 2.11.1 Darstellung natürlicher Zahlen . . . . . . . 2.11.2 Darstellung boolescher Werte . . . . . . . . 2.11.3 Realisierung des Y-Kombinators . . . . . . .
. . . . . . . . . . . . . . . . . . . .
4 4 5 6 6 7 8 8 9 10 11 12 14 15 16 18 20 21 22 23 23
3 Typisierte λ-Kalküle 3.1 λ→ -Curry . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 λ→ -Church . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24 24 28
2
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . .
1 EINLEITUNG
1 Einleitung Kaum 60 Jahre ist es her, daß Konrad Zuse 1941 mit der legendären Z3 die erste programmgesteuerte Rechenmaschine der Welt entwickelte. Auch wenn im Bereich der technologischen Basis seitdem rasante Evolutionssprünge vollbracht wurden, kann dies nicht darüber hinwegtäuschen, daß selbst modernste Rechner nur sehr elementare Operationen durchführen, wenn auch mit hoher Geschwindigkeit. Die geringe Mächtigkeit derartiger Operationen ebenso wie die extreme Hardwarenähe haben in Abhängigkeit von modischen Trends und persönlichen Geschmäckern zum Entwurf einer kaum überschaubaren Vielzahl von Programmiersprachen geführt. Das gemeinsame Ziel aller Sprachen ist es, algorithmische Problemlösungen in einer Art und Weise beschreiben zu können, die einerseits dem menschlichen Geist besser zugänglich ist als eine Ansammlung von Maschineninstruktionen, andererseits aber automatisiert in ebensolche übersetzbar ist. Bei den traditionellen imperativen Sprachen liegt der Schwerpunkt eher im Bereich der einfachen Übersetzbarkeit als im Bestreben, abstrakte Beschreibungsmöglichkeiten zur Verfügung zu stellen. Dementsprechend ist es nicht verwunderlich, daß imperative Sprachen durch ihre relative Maschinennähe alle auf sehr ähnlichen Prinzipien wie Kontrollflußsteuerung und Zuweisungsoperationen beruhen. Die Unterschiede zwischen den Sprachen sind daher eher syntaktischer als konzeptioneller Natur. Funktionale Sprachen hingegen rücken den Beschreibungsaspekt in den Vordergrund. Die Vermutung liegt nahe, daß das gehobene Abstraktionsniveau eine größere Variabilität an Designentscheidungen mit sich bringt und unterschiedliche Sprachen konzeptionell divergieren. Bemerkenswerterweise ist das jedoch keineswegs der Fall. Die meisten funktionalen Sprachen sind sich im Kern sehr ähnlich, was den Eindruck erweckt, daß auch hier vorwiegend der Wunsch nach individuellem syntaktischen Erscheinungsbild die Entstehung neuer Sprachen motiviert hat. Es stellt sich somit die Frage nach Art und Ursprung des gemeinsamen funktionalen Kerns. Welche Grundlage hat die funktionale Programmierung, d.h. was verbindet funktionale Sprachen nach Entfernung aller syntaktischen Eigenheiten? Im Versuch, eine allgemeinen Theorie über Funktionen und Logik zu entwickeln, hat Church (1932/33) ein Modell berechenbarer Funktionen eingeführt, das heute unter der Bezeichnung λKalkül bekannt ist.1 Der λ-Kalkül beschränkt sich auf rein syntaktische Manipulationen textueller Ausdrücke anhand fundamentaler Reduktions- und Konversionsregeln. Dieser ausgeprägte Minimalismus genügt jedoch bereits, um die Eigenschaften sämtlicher rekursiver Funktionen zu beschreiben (Kleene, 1936). Wie Turing (1937) gezeigt hat, ist der Begriff der TuringBerechenbarkeit äquivalent zur λ-Definierbarkeit, so daß im Einklang mit der Churchschen These zu Recht von der Allmächtigkeit des λ-Kalküls gesprochen werden kann. Die meisten funktionalen Sprachen stellen nicht mehr dar als syntaktisch angereicherte Versionen des λ-Kalküls (Barendregt, 1990), so daß es lohnend erscheint, die mathematische Basis einer eigenständigen Betrachtung zu unterziehen. 1
Der Theorieteil jenseits des λ-Kalküls ist allerdings inkonsistent (Kleene & Rosser, 1935).
3
2.1 Syntax
2 UNTYPISIERTER λ-KALKÜL
Die vorliegende Einführung kann das umfangreiche Gebiet nur anreißen und basiert vornehmlich auf Field & Harrison (1988); Reade (1989); Barendregt (1991) und Nipkow (1998). Weiterführende Aspekte finden sich z.B. in Church (1941); Barendregt (1984); Hindley & Seldin (1986).
2 Untypisierter λ-Kalkül 2.1 Syntax Der gesamte λ-Kalkül beschäftigt sich mit dem Begriff des λ-Ausdrucks und den Regeln, nach denen λ-Ausdrücke umgeformt und verändert werden. Ähnlich wie bei den bekannten arithmetischen Ausdrücken ist das Erscheinungsbild eines λ-Ausdrucks auf dem Papier zwar linear, jedoch liegt ihm eine innere hierarchische Struktur zugrunde, die rekursiven Charakter hat. Neben zwei Arten von Basistermen, die quasi Rekursionsanker darstellen, gibt es zwei Konstruktionen, die jeweils zwei Terme zu einem neuen Term zusammenfügen. Definition Seien C und V zwei abzählbare Mengen. Die Elemente von C werden als Konstanten bezeichnet und die Elemente von V als Variablen. Dann ist die Menge Λ der λ-Ausdrücke (Terme) induktiv definiert durch: x∈V ⇒x∈Λ
Variable
c∈C⇒c∈Λ
Konstante Applikation (Kombination)
E1 , E2 ∈ Λ ⇒ (E1 E2 ) ∈ Λ
Abstraktion
x ∈ V, E ∈ Λ ⇒ (λx. E) ∈ Λ
Variablen haben im λ-Kalkül eine ähnliche Bedeutung wie in den (funktionalen) Programmiersprachen. Sie dienen vorwiegend als Stellvertreter für Werte in einem Funktionsrumpf, wobei die Funktionsbildung als Abstraktion dargestellt wird. Im Gegenzug stehen Applikationen offenbar in Zusammenhang mit der Anwendung von Funktionen auf Argumente. Derartige Analogiebetrachtungen sind einerseits zwar hilfreich, dürfen aber andererseits nicht dazu verleiten, Beschränkungen zu sehen, wo keine sind.2 Die Idee der Funktionsanwendung impliziert stillschweigend, daß in einer Applikation (E1 E2 ) der Term E1 eine Funktion beschreibt. Diese Einschränkung macht die Syntax jedoch nicht. Beide beteiligten Terme E1 und E2 können von beliebiger Gestalt sein. Insbesondere muß E1 also nicht die Form einer Abstraktion besitzen. Daraus resultiert eine große Flexibilität im Aufbau und der späteren Verwendung von λ-Ausdrücken. Das äußert sich besonders eindrucksvoll in der Realisierung der Rekursion (Abschnitt 2.10). Zuvor ist jedoch zu klären, auf welche Art die Idee der Funktionsanwendung tatsächlich in den λ-Kalkül zu übertragen ist (Abschnitt 2.6.1). 2
Es ist dem Verständnis sicher zuträglich, den mathematischen Purismus nicht zu übertreiben und pragmatische Querverbindungen zur bekannten Welt der Programmierung herzustellen. Trotzdem sind solche Versuche semantischer Interpretationen mit Vorsicht zu betrachten. Der ganze λ-Kalkül basiert ausschließlich auf syntaktischen Betrachtungen ohne eine Semantik zu unterstellen. Maßgebend ist immer die Syntax.
4
2.2 Currying
2 UNTYPISIERTER λ-KALKÜL
Nicht unmittelbar ersichtlich ist die Rolle der Konstanten. Tatsächlich kann auf die Einführung von Konstanten sogar völlig verzichtet werden ohne dabei an Ausdrucksstärke einzubüßen. Das führt zum reinen λ-Kalkül (Abschnitt 2.11). Wenn Konstanten verwendet werden, dann aus rein pragmatischen Gründen (Abschnitt 2.3). In komplexeren λ-Ausdrücken treten häufig geschachtelte Abstraktionen und mehrfache Applikationen auf (Abschnitt 2.2), was durch die Vielzahl von resultierenden Klammern schnell unübersichtlich wird. Daher erweist es sich als zweckmäßig, einfache Klammerkonventionen einzuführen: • Applikation ist linksassoziativ, d.h. E1 E2 E3 ≡ (E1 E2 )E3 . • Abstraktionen binden so weit wie möglich nach rechts, d.h. beispielsweise λx. E1 E2 ≡ λx. (E1 E2 ) 6≡ (λx. E1 ) E2 und λx. λy. E1 E2 ≡ λx. (λy. E1 E2 ) • Die äußersten Klammern werden weggelassen. Beispiele für λ-Ausdrücke und Klammerkonventionen vollständig geklammert
mit Klammerkonvention
(λx. (y z))
λx. y z
(λx. (λy. ((c x) y)))
λx. λy. c x y
(v ((x (λv. (y v))) x))
v (x (λv. y v) x)
((λf. (λx. (λy. ((f x) y))))(λx. (λy. x)))
(λf. λx. λy. f x y)(λx. λy. x)
2.2 Currying Die Syntax des λ-Kalküls ist schlicht gehalten, so sind insbesondere nur einstellige Funktionen zugelassen. Jede Abstraktion bindet genau eine Variable (Abschnitt 2.4) und niemals mehrere, d.h. Konstruktionen der Art λ(x, y). E sind nicht möglich, allerdings auch nicht erforderlich. Der Raum der Funktionen, die vom kartesischen Produkt zweier Mengen A und B in eine Zielmenge C abbilden, ist mengentheoretisch isomorph zum Raum der Funktionen, die von A in den Raum der Funktionen von B nach C abbilden: (A×B) → C ∼ = A → (B → C). Analoges gilt bei mehr als zwei Mengen im Produkt des Urbildbereichs. Daher kann jede gewünschte mehrstellige Funktion dadurch realisiert werden, daß sie zunächst nur ein Argument entgegennimmt und als Resultat eine Funktion liefert, die das nächste Argument entgegennimmt usw. bis das Endergebnis erhalten wird. Beispielsweise wird das (unzulässige) λ(x, y, z). E ausgedrückt durch λx. (λy. (λz. E)) und mit der Klammerkonvention erhält man übersichtlicher λx. λy. λz. E.3 3
Manche Autoren schreiben an dieser Stelle kürzer λx, y, z. E, was hier jedoch vermieden wird, um nicht unnötige Verwirrung zu stiften.
5
2.3 Bedeutung der Konstanten
2 UNTYPISIERTER λ-KALKÜL
Diese Technik, Mehrstelligkeit auf Einstelligkeit zurückzuführen, wird üblicherweise als Currying bezeichnet. Die Idee ist aber in Wirklichkeit älter als der λ-Kalkül (Schönfinkel, 1924) und dahinter steckt mehr als nur ein syntaktischer Trick, da auf diese Art partielle Funktionsapplikationen ermöglicht werden. In der funktionalen Programmierung führt das unmittelbar auf das Konzept der „Funktionen höherer Ordnung“. 2.3 Bedeutung der Konstanten Ebenso wie Variablen stellen Konstanten Basisterme dar, die nicht aus einfacheren Termen aufgebaut sind. Anders als Variablen werden sie aber niemals durch λ-Abstraktionen gebunden. Während im reinen λ-Kalkül die Menge der Konstanten leer ist (Abschnitt 2.11), ist sie im nicht-reinen λ-Kalkül weitgehend willkürlich und wird im konkreten Fall unter dem Praktikabilitätsaspekt definiert. Zum einen können Konstanten als Funktionssymbole auftreten, die nicht weiter interpretiert werden, also im eigentlichen Sinn „konstant“ sind. Solche sogenannten Konstruktoren dienen zur Realisierung von Datentypen. Zum anderen können Konstanten Funktionssymbole mit vordefinierter Bedeutung darstellen. Für diese sogenannten Primitiven werden je nach Bedarf spezielle Transformationsvorschriften (δ-Regeln) eingeführt. Umformungen von Termen unter Verwendung von δ-Regeln werden als δ-Reduktionen bezeichnet und in der Form E1 →δ E2 notiert. Normalerweise führt das zu einer Termvereinfachung. Beispiele • Typische Konstruktoren sind Zahlensymbole 0, 1, 2, . . . , zur bequemen Darstellung von natürlichen Zahlen oder nil zur Repräsentation der leeren Liste und cons x y zum stückweisen Aufbau einer Liste, z.B. cons 1 (cons 2 nil) als Beschreibung der Liste h1, 2i. Dabei dient cons hier als (quasi-)zweistellige Funktion, die aber nicht interpretiert wird. • Wirklichen Nutzen entfalten die Konstruktoren in Kombination mit Primitiven. Beispielsweise kann plus über δ-Regeln auf den natürlichen Zahlen operieren: plus 1 2 →δ 3 Listen werden über hd und tl manipuliert mit den δ-Regeln hd (cons x y) →δ x und tl (cons x y) →δ y, z.B. hd (cons 1 (cons 2 nil)) →δ 1. 2.4 Freie und gebundene Variablen Aufgrund ihrer hierarchischen Struktur lassen sich λ-Ausdrücke als Termbäume darstellen. Eine im Ausdruck auftretende Variable x wird durch das nächste λx gebunden, das sich im Termbaum oberhalb von x befindet. Das entspricht dem Auftreten eines Formalparameters in einem Funktionsrumpf. Falls x in einem Ausdruck an einer Stelle auftritt, wo es von keiner zugehörigen Abstraktion gebunden wird, dann handelt es sich um ein freies Auftreten und x wird als freie Variable bezeichnet, und zwar auch dann, wenn ein anderes Auftreten von x zu einer Bindung führt.
6
2.5 Syntaktische Äquivalenz
2 UNTYPISIERTER λ-KALKÜL
Mit anderen Worten kann eine Variable zugleich sowohl frei als auch gebunden sein. Jedes Auftreten für sich ist aber entweder frei oder gebunden. Formal wird die Menge der freien Variablen eines λ-Ausdrucks über eine Abbildung F V definiert. Die Frage, ob eine Variable frei ist oder nicht, gewinnt besondere Bedeutung bei der α-Konversion (Abschnitt 2.5.2) und der β-Reduktion (Abschnitt 2.6.1). Definition Die Funktion F V : Λ → P(V ) ordnet jedem λ-Ausdruck eine Menge von freien Variablen zu und ist induktiv aufgebaut: F V (c) = ∅
(∀c ∈ C)
F V (x) = {x}
(∀x ∈ V )
F V (E1 E2 ) = F V (E1 ) ∪ F V (E2 )
(∀E1 , E2 ∈ Λ)
F V (λx. E) = F V (E) \ {x}
(∀x ∈ V, E ∈ Λ)
Beispiel frei
λx
λx. (λx. x y) (x (λx. λy. y z x))
•
λx gebunden
•
gebunden
x
•
gebunden
x
gebunden
λx λy
y
•
x
•
y
z
x tritt dreimal auf und wird dabei stets gebunden, allerdings jedesmal von einer anderen Abstraktion, y tritt sowohl frei als auch gebunden auf und ist damit eine freie Variable, ebenso wie z, das nirgendwo gebunden wird. Rechnerisch ergibt sich: F V (λx. (λx. x y) (x (λx. λy. y z x))) = F V ((λx. x y) (x (λx. λy. y z x))) \ {x}
1×Abstraktion
= (F V (λx. x y) ∪ F V (x) ∪ F V (λx. λy. y z x)) \ {x}
2×Kombination
= ((F V (x y) \ {x}) ∪ F V (x) ∪ (F V (y z x) \ {x, y}) \ {x}
3×Abstraktion
= (({x, y} \ {x}) ∪ {x} ∪ ({y, z, x} \ {x, y}) \ {x}
3×Kombination
= {y, x, z} \ {x} = {y, z} 2.5 Syntaktische Äquivalenz Gebundene Variablen sind eng gekoppelt an die zugehörige λ-Abstraktion, durch die die Bindung realisiert wird. Tatsächlich hat das Auftreten der Variablen im Rumpf der Abstraktion
7
2.5 Syntaktische Äquivalenz
2 UNTYPISIERTER λ-KALKÜL
lediglich den Zweck, die konkrete Stelle der Bindung zu markieren. Jede Notation, die das ermöglicht, kann als syntaktische Variante der Definition des λ-Ausdrucks aus Abschnitt 2.1 angesehen werden. 2.5.1 De-Bruijn-Notation Die Verwendung von Namen für Variablen mag zwar recht natürlich erscheinen, doch wie in Abschnitt 2.5.2 gezeigt wird, ist sie nicht in jeder Hinsicht praktisch. Die Referenzierung des Parameters einer Abstraktion könnte auch beispielsweise durch direkten graphischen Verweis auf den Abstraktionskopf erfolgen, z.B. in der Form: (λx. λy. λz. z y x)(λx. λy. x) ∼ = (λ λ λ • • •)(λ λ •) Solche graphischen Verweise verlassen vom Erscheinungsbild her den linearen Rahmen einer einfachen syntaktischen Beschreibung. Die zugrundeliegende Idee läßt sich aber leicht übertragen, sofern ein Pfeil nicht als absoluter Bezug auf ein λ interpretiert wird, sondern als relative Angabe zum Überspringen einer bestimmten Anzahl von λ’s. Diese Überlegung führt zur De-Bruijn-Notation, wo jedes Auftreten einer Variablen durch eine Zahl ersetzt wird. Dieser sogenannte De-Bruijn-Index gibt die Anzahl der λ’s an, die übersprungen werden müssen, um den Anfang der bindenden Abstraktion zu finden. Beim Auftreten einer freien Variablen wird auf das erste nicht-existente λ verwiesen. Weitere freie Variablen zählen dann weiter hoch. Beispiele (λx. λy. λz. z y x)(λx. λy. x) ∼ = (λ λ λ 0 1 2)(λ λ 1) λx. z y x ∼ = λ120 λx. x (λy. y x) ∼ = λ 0 (λ 0 1) Wie das letzte Beispiel zeigt, liegt ein Nachteil der De-Bruijn-Notation in ihrer Unübersichtlichkeit, die dadurch entsteht, daß gleiche Indizes sich auf unterschiedliche Variablen beziehen und gleiche Variablen mit unterschiedlichen Indizes auftreten können. Vorteilhaft ist die eindeutige Repräsentation syntaktisch äquivalenter Terme, d.h. wenn zwei λ-Ausdrücke strukturell identische Termbäume besitzen und Variablen in der gleichen Art und Weise binden, dann sind sie auch Zeichen für Zeichen gleich. Daß diese Eigenschaft nicht selbstverständlich ist, läßt sich daran erkennen, daß die übliche Notation sie nicht besitzt. Damit beschäftigt sich der folgende Abschnitt. 2.5.2 α-Konversion Wenn nicht wie bei der De-Bruijn-Notation vom Konzept der Namensgebung für Variablen abgewichen werden soll, muß geklärt werden, unter welchen Umständen zwei λ-Ausdrücke als
8
2.5 Syntaktische Äquivalenz
2 UNTYPISIERTER λ-KALKÜL
gleich aufgefaßt werden. Da die Namen gebundener Variablen nur Mittel zum Zweck sind, aber ihnen keine eigenständige Bedeutung zukommt, liegt es nahe, Terme zu identifizieren, die sich nur in der Benennung gebundener Variablen unterscheiden. Definition Zwei λ-Ausdrücke E1 und E2 heißen α-konvertierbar oder syntaktisch äquivalent, notiert als E1 ≡ E2 (auch E1 =α E2 ), wenn sie bis auf Umbenennung gebundener Variablen gleich sind.4 Formal ist α-Konversion induktiv beschreibbar: λx. E ≡ λy. E[y/x] falls y ∈ / F V (E) E1 ≡ E1′ ⇒ E1 E2 ≡ E1′ E2 E2 ≡ E2′ ⇒ E1 E2 ≡ E1 E2′ E ≡ E ′ ⇒ λx. E ≡ λx. E ′ Der erste Fall ist der Kern der Definition und beschreibt den Übergang von einer gebundenen Variablen zu einer anderen. Die restlichen drei Fälle sagen nur aus, daß α-Konversion kompatibel zum strukturellen Aufbau von λ-Ausdrücken ist. Beliebige Teilausdrücke können unabhängig vom Kontext betrachtet werden, in dem sie auftreten. Die Notation E[y/x] bezieht sich auf die Substitution von y für jedes freie Auftreten von x im Ausdruck E. Da der Substitutionsbegriff auch für die β-Reduktion (Abschnitt 2.6.1) bedeutsam ist und einige Subtilitäten in der Anwendung birgt, lohnt sich eine Konkretisierung der intuitiven Vorstellung, was im folgenden Abschnitt 2.5.3 vorgenommen wird. Dort finden sich dann auch Beispiele für α-Konversionen. 2.5.3 Substitution Definition Die Substitution eines Terms E für eine Variable v in einem Term E ′ , geschrieben E ′ [E/v], ersetzt jedes freie Vorkommen von v in E ′ durch E: v[E/v] ≡ E x[E/v] ≡ x
für x 6≡ v
c[E/v] ≡ c
für c ∈ C
(E1 E2 )[E/v] ≡ (E1 [E/v])(E2 [E/v]) (λv. E1 )[E/v] ≡ λv. E1 für x 6≡ v und x ∈ / F V (E)
(λx. E1 )[E/v] ≡ λx. E1 [E/v] bzw. (λx. E1 )[E/v] ≡ λy. (E1 [y/x])[E/v] 4
für x 6≡ v und x ∈ F V (E) und y ∈ / F V (E1 E)
Das Zeichen ‚≡‘ für syntaktische Äquivalenz wird nicht nur bei α-Konversion im engeren Sinne angewandt, sondern häufig auch dann, wenn die direkte Austauschbarkeit zweier Ausdrücke betont werden soll, etwa bei der Einführung von Abkürzungen wie I ≡ λx. x. Manche Autoren bevorzugen dann ‚=‘, andere hingegen meiden es wegen Verwechslungsgefahr mit ‚=β ‘, dem Zeichen für β-Konvertibilität (Abschnitt 2.6.1). Häufig wird α-Konversion als so elementar betrachtet, daß sie implizit „bei Bedarf“ durchgeführt wird ohne besondere Erwähnung.
9
2.6 β-Reduktion
2 UNTYPISIERTER λ-KALKÜL
Die Substitution erfolgt allgemein durch rekursiven Abstieg in die Struktur eines λ-Ausdrucks. Interessant sind die letzten drei Fälle, die eine nähere Diskussion erfordern: 1. (λv. E1 )[E/v] ≡ λv. E1 Da die Abstraktion λv. E1 jedes eventuelle freie Auftreten von v in E1 bindet, erfolgt kein Abstieg in den Rumpf der Abstraktion. Würde die Substitution auf E1 ausgedehnt werden, hätte dies eine Änderung der Bindungen zur Folge. Das ist nicht das Ziel der Substitution. 2. (λx. E1 )[E/v] ≡ λx. E1 [E/v] für x 6≡ v und x ∈ / F V (E) Wenn eine Abstraktion erreicht wird, die nicht die fragliche Variable v bindet, sondern eine andere Variable x, dann muß v im Rumpf ersetzt werden. Dabei darf aber nach wie vor keine Änderung der Bindungsverhältnisse erfolgen. Während beim vorigen Fall Bindungen nicht verloren gehen durften, muß hier beachtet werden, daß keine neuen entstehen dürfen. Durch das Einsetzen von E für v im Rumpf würde aber jedes freie Auftreten von x in E fälschlicherweise gebunden werden. Daher ist diese Regel nur anwendbar, wenn x nicht frei in E auftritt. Andernfalls ist die Anwendung der folgenden Regel erforderlich. 3. (λx. E1 )[E/v] ≡ λy. (E1 [y/x])[E/v] für x 6≡ v und x ∈ F V (E) und y ∈ / F V (E1 E) Falls x frei in E auftritt, dann muß eine α-Konversion der Abstraktion erfolgen, bevor die Substitution durchgeführt werden kann. Dabei ist der Name der Variablen so zu ändern, daß keine Kollision mit frei auftretenden Variablen in E mehr stattfinden kann. Allerdings darf es auch nicht zu Kollisionen mit freien Variablen in E1 kommen, da auch auf diese Art neue Bindungen zustande kämen (das ist die übliche Forderung der α-Konversion). Beispiele λx. x =α λy. x[y/x] ≡ λy. y λx. x (λx. x) y =α λz. (x (λx. x) y)[z/x]
Umbenennung x → z
≡ λz. (x (λx. x))[z/x] y[z/x]
1×Kombination
≡ λz. x[z/x] (λx. x)[z/x] y
1×Kombination, 1×Variable
≡ λz. z (λx. x) y
1×Variable, 1×Abstraktion
∄E ∈ Λ : λx. x y =α λy. E, denn freie Variablen müssen frei bleiben: y ∈ F V (x y) Weitere Substitutionsbeispiele finden sich bei der β-Reduktion (Abschnitt 2.6.1). 2.6 β-Reduktion Bislang stand die Betrachtung notationeller und syntaktischer Aspekte des λ-Kalküls im Vordergrund. Wie in Abschnitt 2.1 aber schon angedeutet wurde, ist der grundlegende Ansatz für die Modellierung des Verhaltens von Funktionen, sich die zentralen Mechanismen Abstraktion zur Funktionsbildung und Applikation zur Funktionsauswertung zunutze zu machen.
10
2.6 β-Reduktion
2 UNTYPISIERTER λ-KALKÜL
Im Rahmen der δ-Reduktion (Abschnitt 2.3) kam die Idee von Transformationsvorschriften auf, um λ-Ausdrücke in bestimmtem Sinne auszuwerten. Dort gab es für jede Primitive einen Satz von δ-Regeln, die spezielle Auswertungsvorschriften darstellen. Diese haben aber einen eher externen Charakter, da sie nichts mit der Auswertung von Funktionen zu tun haben, die mit dem Mittel der Abstraktion direkt im λ-Kalkül gebildet werden, sondern ausschließlich auf passende Primitiven zugeschnitten sind. Die eigentliche Mächtigkeit des λ-Kalküls erwächst aus dem allgemeinen Auswertungsmechanismus für Applikationen, der unter der Bezeichung β-Reduktion bekannt ist. Dabei wird eine Applikation, deren erster Teilausdruck eine Abstraktion ist, dadurch transformiert, daß der zweite Teilausdruck im Rumpf der Abstraktion für die gebundene Variable substituiert wird. Dadurch wird die Abstraktion aufgelöst, was im allgemeinen eine Vereinfachung des Gesamtterms darstellt, entsprechend der Auswertung einer Funktion. 2.6.1 Definition der β-Reduktion Definition →β ⊂ Λ × Λ ist die kleinste Relation, die folgende Regeln erfüllt: (λx. E1 )E2 →β E1 [E2 /x] E1 →β E1′ ⇒ E1 E2 →β E1′ E2 E2 →β E2′ ⇒ E1 E2 →β E1 E2′ E →β E ′ ⇒ λx. E →β λx. E ′ →β wird als β-Reduktion (auch Kontraktion) bezeichnet und üblicherweise wie oben in InfixNotation verwendet: E1 →β E2 :⇔ (E1 , E2 ) ∈ →β . Ein Ausdruck der Form (λx. E1 )E2 ist Kandidat für die Durchführung einer β-Reduktion und heißt β-Redex (reducible expression). Das Ergebnis heißt Redukt. Ein λ-Ausdruck, der keinen β-Redex enthält, ist in (β-)Normalform. Wenn ENF in Normalform ist und eine Folge von β-Reduktionen E →β . . . →β ENF existiert, dann ist ENF Normalform von E. Ähnlich wie bei der α-Konversion (Abschnitt 2.5.2) steckt auch hier die wesentliche Aussage im ersten Fall (zum Begriff der Substitution siehe Abschnitt 2.5.3). Die restlichen Fälle drücken nur die Kompatibilität der β-Reduktion mit dem Aufbau von λ-Ausdrücken aus. Der Begriff der Normalform wird in Abschnitt 2.8 genauer untersucht. Die obige Definition unterstreicht den relationalen Charakter der Reduktion. Alternativ können Reduktionsregeln aber auch als eine Menge von Schlußregeln aufgefaßt werden, was häufig so notiert wird: (λx. E1 )E2 →β E1 [E2 /x]
E1 →β E2
E1 →β E2
E1 →β E2
E1 E →β E2 E
EE1 →β EE2
λx. E1 →β λx. E2
11
2.6 β-Reduktion
2 UNTYPISIERTER λ-KALKÜL
Beispiele • Die Identitätsfunktion reproduziert unter β-Reduktion erwartungsgemäß ihr Argument: (λx. x)(x y) →β x[x y/x] ≡ x y • Argumente können von beliebig komplexer Gestalt sein. Insbesondere kann es sich auch um Funktionen handeln, d.h. Funktionen höherer Ordnung erfordern im λ-Kalkül keinerlei Sonderbehandlung: (λx. x y)(λx. x z) →β (x y)[(λx. x z)/x] ≡ (λx. x z) y →β (x z)[y/x] ≡ yz • Bei der β-Reduktion erfolgen Substitutionen, wobei es zu Namenskonflikten kommen kann (name clashes), die zwischenzeitliche α-Konversionen erforderlich machen, bevor die Reduktion fortgesetzt werden kann: (λx. λy. x y)(x y) →β (λy. x y)[x y/x]
y ∈ F V (x y)
=α (λz. x z)[x y/x]
z∈ / F V (x y)
≡ λz. (x y) z Meist erfolgt die α-Konversion dabei ohne explizite Erwähnung, da sie nichts Substantielles ausdrückt, sondern nur eine „ungeschickte“ Wahl von Bezeichnern kompensiert. Da die β-Reduktion einem einzelnen Berechnungsschritt entspricht, der Weg zum Endergebnis, der Normalform, aber im allgemeinen aus einer Abfolge von Berechnungen besteht, ist der reflexiv-transitive Abschluß der β-Reduktion nützlich. Definition →∗β bezeichnet den reflexiv-transitiven Abschluß der Relation →β , d.h. E →∗β E E1 →β E2 ⇒ E1 →∗β E2 E1 →∗β E2 ∧ E2 →∗β E3 ⇒
Reflexivität Einzelschritt
E1 →∗β
E3
Transitivität
2.6.2 Konfluenz der β-Reduktion Ein λ-Ausdruck kann eine beliebige Zahl von Abstraktionen und Applikationen enthalten, was bedeutet, daß es mehr als einen β-Redex geben kann. Unter Umständen muß eine Wahl getroffen werden, welcher Redex als nächstes reduziert wird.
12
2.6 β-Reduktion
2 UNTYPISIERTER λ-KALKÜL
Beispiel (λx. x x)((λz. z y) x)
β
(λx. x x)(x y)
β
(λz. z y) x (x y)
β
β
β
(λz. z y) x ((λz. z y) x)
x y (x y)
β
β
x y ((λz. z y) x) Im Beispiel führen alle Reduktionsfolgen zum gleichen Ergebnis. Allerdings ist auch zu erkennen, daß die β-Reduktion nicht die Diamant-Eigenschaft besitzt.5 Somit stellt sich unmittelbar die Frage, ob das Ergebnis grundsätzlich eindeutig ist oder ob es Terme gibt, wo verschiedene Reduktionsfolgen sich in unterschiedliche Richtungen entwickeln und zu verschiedenen Resultaten führen können. Günstigerweise läßt sich zeigen, daß β-Reduktion konfluent ist (siehe z.B. Hindley & Seldin, 1986; Barendregt, 1984). Da Funktionen bei gegebenen Eingabewerten ein eindeutiges Ergebnis produzieren sollten, ist diese Erkenntnis von fundamentaler Bedeutung für den λ-Kalkül und seinen Ansatz, Funktionen über λ-Ausdrücke darzustellen. Theorem (Church-Rosser) β-Reduktion ist konfluent, d.h. ∀E, E1 , E2 ∈ Λ gilt: E →∗β E1 ∧ E →∗β E2 ⇒ ∃E3 ∈ Λ : E1 →∗β E3 ∧ E2 →∗β E3 Die Konfluenzeigenschaft läßt sich gut graphisch veranschaulichen: E ∗
β
β
E1
∗
E2 β
∗
∗
β
E3 Wenn ausgehend von einem λ-Ausdruck durch unterschiedliche Reduktionsfolgen zwei verschiedene Terme erhalten werden, dann ist es also in jedem Fall möglich, durch Fortsetzung der Reduktion die abweichenden Zwischenergebnisse wieder zusammenzuführen. 5
Eine binäre Relation → auf einer Menge A, → ⊂ A × A, hat die Diamant-Eigenschaft, wenn ∀x, y1 , y2 ∈ A gilt: x → y1 ∧ x → y2 ⇒ ∃z ∈ A : y1 → z ∧ y2 → z.
13
2.6 β-Reduktion
2 UNTYPISIERTER λ-KALKÜL
Eine Konsequenz davon ist die Eindeutigkeit der Normalform eines λ-Ausdrucks. Wenn es zwei verschiedene Normalformen gäbe, dann müßten sie aufgrund der Konfluenz ein gemeinsames Redukt besitzen. Da Normalformen nicht reduzierbar sind, kann das aber nicht sein. Als weitere Konsequenz folgt die Gewißheit, daß es niemals erforderlich ist, β-Reduktionen rückgängig zu machen, um zur Normalform zu gelangen. β-Reduktion erfordert grundsätzlich keine Backtracking-Strategie, da jeder Weg die Möglichkeit offenhält, das Ziel zu erreichen.6 Das bedeutet jedoch nicht, daß auch tatsächlich jeder mögliche Weg zum Ziel führt. Insbesondere sichert die Konfluenz auch nicht zu, daß es überhaupt eine Normalform gibt (siehe Abschnitt 2.8). 2.6.3 β-Konversion Während die α-Konversion von ihrer Natur her symmetrisch ist — der Weg von links nach rechts ist völlig gleichwertig zu dem von rechts nach links —, ist die β-Reduktion zunächst von asymmetrischer gerichteter Form. Das paßt gut zu der Vorstellung, daß ein Ergebnis aus Argumenten errechnet wird, nicht die Argumente aus dem Ergebnis. Eine mögliche Sichtweise auf den λ-Kalkül ist jedoch, ihn als Gleichungskalkül zu betrachten, d.h. die β-Konvertibilität von λ-Ausdrücken zu untersuchen. Zu diesem Zweck ist die Betrachtung der Umkehrung der β-Reduktion sinnvoll, wobei man von β-Abstraktion oder β-Expansion spricht. Definition =β bezeichnet den reflexiv-symmetrisch-transitiven Abschluß der Relation →β , d.h. E1 →∗β E2 ⇒ E1 =β E2
Reduktion
E1 =β E2 ⇒ E2 =β E1
Symmetrie
E1 =β E2 ∧ E2 =β E3 ⇒ E1 =β E3
Transitivität
Wenn E1 =β E2 gilt, werden E1 und E2 als β-konvertierbar bezeichnet. Es sei ausdrücklich betont, daß β-Konversion nicht den symmetrischen Abschluß von →∗β darstellt. Natürlich gilt E1 →∗β E2 ∨ E2 →∗β E1 ⇒ E1 =β E2 , aber aus der β-Konvertibilität E1 =β E2 kann keineswegs darauf geschlossen werden, daß es einen direkten Weg von E1 zu E2 (oder umgekehrt) gibt: E1 =β E2 6⇒ E1 →∗β E2 ∨ E2 →∗β E1 . Das liegt daran, daß β-Konversion auf dem Niveau einzelner Reduktionsschritte arbeiten kann, wobei möglicherweise eine Art Zickzackkurs bei der Konversion beschritten wird. 6
Das ist ein wesentlicher Unterschied zwischen funktionalen und logischen Programmiersprachen, wo Backtracking zum Sprachkonzept gehört.
14
2.7 η-Reduktion
2 UNTYPISIERTER λ-KALKÜL
Beispiel Aus E2 →∗β E1 , E2 →∗β E3 , E4 →∗β E3 , E4 →∗β E5 kann auf E1 =β E5 geschlossen werden, z.B. übersichtlich als Ableitungsbaum dargestellt: E2 →∗β E1
E4 →∗β E3
E2 =β E1
E2 →∗β E3
E4 =β E3
E4 →∗β E5
E1 =β E2
E2 =β E3
E3 =β E4
E4 =β E5
E1 =β E3
E3 =β E5 E1 =β E5
oder graphisch veranschaulicht: E2 β
E4 β
E1
β
E3
⇒ E1 =β E5
β
E5
Das Beispiel deutet bereits an, daß der Nachweis der β-Konvertierbarkeit zweier λ-Ausdrücke anhand der Definition der β-Konversion nicht in systematischer Weise erfolgen kann. Die Wahl eines β-Redex für die β-Reduktion mag nicht eindeutig sein, aber es gibt an einem Verzweigungspunkt nur endlich viele Möglichkeiten fortzufahren. Bei der β-Expansion hingegen gibt es immer unendlich viele Möglichkeiten, einen „Rückschritt“ durchzuführen. Durch wiederholte Anwendung der Konfluenz-Eigenschaft läßt sich jedoch zeigen, daß im Fall E1 =β E2 ein gemeinsames Redukt von E1 und E2 existieren muß:7 E1 =β E2 ⇔ ∃E ∈ Λ : E1 →∗β E ∧ E2 →∗β E Die Frage, ob zwei λ-Ausdrücke E1 und E2 β-konvertierbar sind, ist allerdings nur dann entscheidbar, wenn (und nur wenn) beide Ausdrücke Normalformen besitzen; diese sind für βkonvertierbare Ausdrücke identisch. Die Frage nach der β-Konvertierbarkeit kann somit auf die Frage nach der Bestimmung der Normalformen zurückgeführt werden. 2.7 η-Reduktion Auf relationalem Niveau betrachtet, stellen Funktionen keine Berechnungsvorschriften dar, sondern Mengen von Urbild-Bild-Paaren. In diesem Sinne sind Funktionen als gleich anzusehen, die sich gleich verhalten, d.h. als Relationen identisch sind. Wenn x nicht frei in E1 vorkommt, ist der Ausdruck (λx. E1 x)E2 ein β-Redex mit dem Redukt E1 E2 . Der Ausdruck λx. E1 x verhält sich also identisch zu E1 , ist aber für sich gesehen nicht β-reduzierbar. 7
Diese Aussage folgt nicht nur aus der Konfluenz, sondern sie ist sogar äquivalent dazu. Allgemein sagt man, daß eine Relation → ⊂ A × A (mit geeignetem A) die Church-Rosser-Eigenschaft besitzt, wenn gilt: ∗ ∗ E1 ↔ E2 ⇔ ∃E ∈ A : E1 →∗ E ∧ E2 →∗ E. Dabei ist →∗ der reflexiv-transitive und ↔ der reflexivsymmetrisch-transitive Abschluß von →.
15
2.8 Reduktionstrategien
2 UNTYPISIERTER λ-KALKÜL
Eine Möglichkeit, die extensionale Sicht auf den Funktionsbegriff in den λ-Kalkül zu übertragen, besteht darin, zur Vereinfachung derartiger Abstraktionen die sogenannte η-Reduktion einzuführen: λx. E1 x →η E1
falls x ∈ / F V (E1 )
Die η-Reduktion ist zwar gelegentlich nützlich, bringt allerdings keinen Gewinn an Ausdrucksstärke mit sich und ist in diesem Sinn als entbehrlich zu betrachten. Da die η-Reduktion konfluent ist, entstehen durch ihre Verwendung aber in keinem Fall Schwierigkeiten.8 Beispiel λx. (λy. z y) x →η λy. z y →η z λx. λy. z y x 6→η λy. z y
wegen λx. λy. z y x ≡ λx. (λy. z y x)
2.8 Reduktionstrategien Die Konfluenz der β-Reduktion garantiert die Eindeutigkeit von Normalformen, macht aber keine Aussage über ihre Existenz. Das Problem besteht darin, daß es λ-Ausdrücke gibt, die unter β-Reduktion unendliche Reduktionsfolgen erzeugen, d.h. der β-Reduktionsprozeß terminiert möglicherweise nicht. Beispiele Terme können sich invariant verhalten: (λx. x x)(λx. x x) →β (λx. x x)(λx. x x) →β . . . oder bei der Reduktion sogar komplexer werden statt einfacher, wie es der Begriff „Reduktion“ nahezulegen scheint: (λx. x x x)(λx. x x x) →β (λx. x x x)(λx. x x x)(λx. x x x) →β (λx. x x x)(λx. x x x)(λx. x x x)(λx. x x x) →β . . . In beiden Beispielen gibt es in jedem Schritt nur einen einzigen Redex, so daß keine Wahlmöglichkeit besteht, um einen anderen Weg einzuschlagen. Es folgt sofort, daß beide λ-Ausdrücke keine Normalform besitzen, die Konfluenz spielt dabei keine Rolle. Die Situation kann jedoch diffiziler sein, weil die Existenz einer nichtterminierenden Reduktionsfolge nicht impliziert, daß alle Reduktionsfolgen endlos sind.
8
Das trifft auf die nützlicheren δ-Regeln übrigens nur zu, wenn sie hinreichend gutartig strukturiert sind. Tatsächlich können pathologisch konstruierte δ-Regeln die Konfluenz zerstören. Da δ-Regeln aber grundsätzlich entbehrlich sind (Abschnitt 2.11), erwächst daraus kein Problem für den λ-Kalkül als Ganzes.
16
2.8 Reduktionstrategien
2 UNTYPISIERTER λ-KALKÜL
Beispiel (λy. c)((λx. x x x)(λx. x x x))
β
β
(λy. c)((λx. x x x)(λx. x x x)(λx. x x x))
c
β
β
(λy. c)((λx. x x x)(λx. x x x)(λx. x x x)(λx. x x x))
β
β
.. . In jedem Schritt gibt es zwei Redexe. Fortgesetzte Reduktion des Arguments führt zu einer endlosen Reduktionsfolge, die Reduktion des anderen Redex führt aber jederzeit in einem einzigen Schritt zur Normalform.9 Im allgemeinen ist es also wenig hilfreich, einen beliebigen Redex auszuwählen und zu reduzieren. Vielmehr ist es angebracht, systematisch vorzugehen, also eine Reduktionsstrategie zur Auswahl des jeweils nächsten Redex zu verfolgen. Definition Bei der Leftmost-Outermost-Reduktion, auch Normal-Order-Reduktion oder kurz NOR, wird jeweils der Redex als nächstes reduziert, der am weitesten links im Ausdruck beginnt. Bei der Leftmost-Innermost-Reduktion, auch Applicative-Order-Reduktion oder kurz AOR, wird jeweils der Redex als nächstes reduziert, der am weitesten links im Ausdruck endet. Anders ausgedrückt wird bei der NOR von links kommend der erste Redex reduziert, der nicht in einem anderen Redex enthalten ist. Bei der AOR hingegen wird ebenfalls von links kommend der erste Redex ausgewählt, der keinen anderen Redex enthält.10 Um die Eigenschaften dieser Reduktionsstrategien zu verstehen, ist eine sorgfältige Betrachtung des obigen Beispiels empfehlenswert, da es einen fundamentalen Einblick in die strukturellen Eigenschaften der β-Reduktion gestattet. Informell ausgedrückt läßt sich feststellen: Jeder λAusdruck mit (mindestens) einer nichtterminierenden Reduktionsfolge enthält (mindestens) einen kleinsten Teilausdruck, dessen „kontraproduktive“ Reduktion Ursache für die Divergenz ist. Wenn der Ausdruck als Ganzes eine Normalform besitzt, dann muß es eine Möglichkeit geben, den störenden Teilausdruck durch eine strategisch geschickt gewählte Reduktion zu beseitigen. 9
Daß es jederzeit möglich ist, zur Normalform zu gelangen, sofern sie existiert, wird durch die Konfluenz garantiert. Im allgemeinen ist dazu aber mehr als ein Schritt erforderlich. 10 Es gibt diverse weitere Reduktionsstrategien (Parallel Outermost, Parallel Innermost, Fully Parallel, Varianten, die auf Markierung von Teilausdrücken vor und während der Reduktion basieren, . . . ), die im Rahmen dieser Einführung aber nicht weiter betrachtet werden.
17
2.9 Aufrufmechanismen höherer Programmiersprachen
2 UNTYPISIERTER λ-KALKÜL
In diese Überlegung geht zum einen ein, daß Normalformen keine Redexe enthalten, und zum anderen, daß bei einer Reduktion ein Argument verschwinden kann, nämlich genau dann, wenn eine Funktion ihr Argument im Rumpf gar nicht benutzt, also keine Variable tatsächlich bindet. Ein Redex, der momentan nicht Teil eines anderen Redex ist, kann infolge der Reduktion eines weiter links stehenden Redex seinen Outermost-Status verlieren und möglicherweise wegreduziert werden.11 Der am weitesten links stehende Redex muß aber über kurz oder lang in jedem Fall reduziert werden, da er unter keinen Umständen verschwinden kann. Wenn man diesen Leftmost-Outermost-Redex in jedem Reduktionsschritt auswählt, ist somit sichergestellt, daß nur dann eine divergente Reduktionsfolge erzeugt wird, wenn es gar keine terminierende Folge gibt. Theorem (Standardisierung/Normalisierung) Iterierte Normal-Order-Reduktion führt zur Normalform eines λ-Ausdrucks, falls diese existiert. AOR hingegen ist eine unsichere Strategie, da sie riskiert, Teilausdrücke zu reduzieren, die bei einer anderen Reduktionsreihenfolge wegfallen würden und damit Ursache einer vermeidbaren Divergenz sein können.12 Im Zusammenhang mit Programmiersprachen ist sie aber trotzdem interessant, da sie Effizienzvorteile bietet (siehe auch Abschnitt 2.9). Beispiel (λx. (λy. y y) x)((λx. x) z)
N OR
(λy. y y) ((λx. x) z)
AOR
N OR
(λx. x x)((λx. x) z)
(λx. x) z ((λx. x) z)
AOR
N OR
(λx. x x) z
z ((λx. x) z) N OR
AOR
zz Im Beispiel auf der Vorseite wird die endlose Reduktionskette durch AOR erzielt und die Termination in einem Schritt durch NOR. 2.9 Aufrufmechanismen höherer Programmiersprachen Die von höheren Programmiersprachen beim Funktionsaufruf verwendeten Mechanismen sind mit dem λ-Kalkül eng verflochten und in der Praxis höchst bedeutsam. Daher soll an dieser Stelle kurz ein Zusammenhang mit den eben diskutierten Reduktionsstrategien hergestellt werden. 11
Mit Ω ≡ (λx. x x)(λx. x x) in (λx. x)(λx. y) Ω stellt der bekanntermaßen nichtterminierende Teilausdruck Ω einen äußersten Redex dar, der verschwinden kann: (λx. x)(λx. y) Ω →β (λx. y) Ω →β y. 12 Es wäre relativ unproblematisch, wenn AOR nur gelegentlich einen „ungünstigen“ Redex auswählen würde. Das Problem liegt darin, daß AOR diese Wahl aber unendlich oft treffen kann.
18
2.9 Aufrufmechanismen höherer Programmiersprachen
2 UNTYPISIERTER λ-KALKÜL
Die Reduktion eines λ-Ausdrucks zur Normalform erfordert Substitutionen, die potentiell Namenskonflikte hervorrufen können und α-Konversionen erforderlich machen. Diese lassen sich durch eine Abschwächung des Begriffs der Normalform vermeiden. Die Idee dabei ist in erster Linie, λ-Abstraktionen möglichst lange unausgewertet zu lassen, d.h. erst dann den Funktionsrumpf zu betrachten, wenn ein Argument zur Verfügung steht, das im Rumpf substituiert werden muß. Definition Ein λ-Ausdruck E ist in weak head-normal form (WHNF), wenn E eine Konstante ist, eine λ-Abstraktion oder eine partiell applizierte konstante Funktion:13 E∈C E ≡ λx. E ′
(∃E ′ ∈ Λ)
E ≡ P E1 E2 . . . En ∧ Arit¨at(P ) > n
(∃P ∈ C, n ∈ IN, E1 , . . . , En ∈ Λ)
Wenn ein Ausdruck nur bis zur WHNF reduziert wird, können keine Namenskonflikte auftreten, da eine Variable nur rechts von dem sie bindenden λ frei sein kann, dieser Rumpf aber zunächst nicht ausgewertet wird. Wenn die Auswertung dann im Rahmen der β-Reduktion einer Applikation erfolgt, wird die freie Variable durch den zu substituierenden Ausdruck ersetzt. Dieser enthält keine freien Variablen, die bei der weiteren Auswertung problematisch sein könnten. Beim Aufruf von Funktionen werden im wesentlichen zwei verschiedene Ansätze mit einigen Varianten verfolgt, die sich im Zeitpunkt der Auswertung der Argumente unterscheiden. Auf der einen Seite steht die Idee, daß die Funktionsauswertung dadurch besonders effizient implementierbar wird, daß ihre Argumente eine einfache Form haben (z.B. die eines maschinennahen Datentyps), insbesondere dann, wenn sie mehrfach im Funktionsrumpf auftreten. Daher werden zuerst die Argumente ausgewertet, um diese einfache Form zu erhalten, und erst dann wird die Funktion betrachtet. Diese Strategie wird als Call by Value oder Eager Evaluation bezeichnet und entspricht einer Applicative-Order -Reduktion zur WHNF, d.h. Redexe innerhalb einer Abstraktion, die die AOR normalerweise auswählen würde, werden ignoriert. Auf der anderen Seite steht die Überlegung, daß es relativ häufig vorkommt, insbesondere im Zusammenhang mit Fallunterscheidungen, daß Funktionen ihre Argumente gar nicht benötigen. Das stellt sich in der Regel erst zur Laufzeit des Programms heraus, weshalb die Auswertung der Argumente verzögert wird bis feststeht, daß sie tatsächlich gebraucht werden. Diese — etwas unglücklich — als Call by Name bezeichnete Strategie entspricht einer Normal-Order Reduktion zur WHNF. Die reine Call-by-Name-Strategie wertet ein und dasselbe Argument möglicherweise mehrfach aus, nämlich dann, wenn es mehrfach im Funktionsrumpf auftritt (und auch gebraucht wird). Die rein textuell basierte NOR läßt dieses Verhalten auch erwarten. Da Funktionen aber in funktionalen Sprachen keine Seiteneffekte haben können, führt jede Auswertung zum gleichen Ergebnis, so daß eine einmalige Auswertung genügen würde. Die Umsetzung dieser Erkenntnis 13
. . . oder eine einzelne Variable, aber in diesem Kontext sind normalerweise nur geschlossene λ-Ausdrücke von Interesse, die keine freien Variablen enthalten.
19
2.10 Rekursion
2 UNTYPISIERTER λ-KALKÜL
wird als Call by Need oder auch Lazy Evaluation 14 bezeichnet und ist im λ-Kalkül nicht mehr direkt darstellbar, da dort keine gemeinsamen Teilausdrücke repräsentiert werden können. Beispiel (λx. plus x x)((λx. x) 1)
plus ((λx. x) 1) ((λx. x) 1)
Call-by-Name
Call-by-Value
Call-by-Name
(λx. plus x x) 1
plus 1 ((λx. x) 1)
Call-by-Value
Call-by-Name
plus 1 1 δ
2 2.10 Rekursion In Programmen, die in höheren Programmiersprachen verfaßt sind, tragen Funktionen üblicherweise Namen. Der Selbstaufruf einer rekursiven Funktion erfolgt dann einfach und naheliegend durch Verwendung dieses Namens im Funktionsrumpf, etwa in der Art: DEF fac == λn. cond(= n 0) 1 (∗ n (fac (− n 1))) Der λ-Kalkül hingegen beschreibt anonyme Funktionen, d.h. Funktionen werden keine Namen zugeordnet, die sie zur Referenzierung verwenden könnten. Das läßt auf den ersten Blick daran zweifeln, daß Rekursion überhaupt im λ-Kalkül repräsentierbar ist. Allerdings wäre er nicht Turing-mächtig, wenn es keine Möglichkeit dazu gäbe. Übertragen auf das obige Ad-hoc-Beispiel besteht das Problem also gewissermaßen darin, einen Weg zu finden, der es gestattet, anstelle des ‚?‘ im folgenden Ausdruck den Ausdruck selbst wieder einzusetzen: λn. cond(= n 0) 1 (∗ n (? (− n 1))) Durch den Abstraktionsmechanismus kann die fragliche Stelle im Funktionsrumpf zunächst ordentlich benannt werden, so daß ein gültiger λ-Ausdruck entsteht, der im Folgenden als fac′ bezeichnet wird: fac′ ≡ λh. λn. cond(= n 0) 1 (∗ n (h (− n 1))) Im Rahmen einer Applikation könnte dann eine „Fortsetzungsfunktion“ für die weitere Berechnung substituiert werden. Dabei hat sich das Problem nun dahingehend verlagert, daß die 14
Manche Autoren betonen, daß bei Lazy Evaluation im Gegensatz zu Call by Need Konstruktorargumente nicht ausgewertet werden. Das dient vor allem der Realisierung „unendlicher“ Datentypen. Im wesentlichen geht es dabei aber um Implementierungsaspekte mit Blick auf die Funktionsweise von Primitiven, was für die vorliegende Arbeit nicht von Interesse ist.
20
2.11 Der reine λ-Kalkül
2 UNTYPISIERTER λ-KALKÜL
Funktion quasi ihre eigene Fortsetzungsfunktion darstellt. Der Gordische Knoten wird dadurch aufgelöst, daß der Fixpunktkombinator Y ins Spiel kommt. Dieser ermittelt den kleinsten Fixpunkt einer Funktion f , besitzt also die Eigenschaft Yf =β f (Yf ). Da das hier interessierende f eine Funktion als Resultat liefert, ist der Fixpunkt selbst eine Funktion und die Frage, in welcher Beziehung er der kleinste ist, kann an dieser Stelle nicht weiter vertieft werden (siehe z.B. Stoy, 1977). Der Trick besteht nun darin, statt fac, das kein λ-Ausdruck ist, Y fac′ zu verwenden und Ausdrücke unter Ausnutzung der zentralen Eigenschaft des Fixpunktkombinators auszuwerten. Allgemein wird eine rekursive Funktion f mit dem Rumpf E, in dem die Variable f als Selbstbezug verwendet wird, repräsentiert durch den Ausdruck Y(λf. E). Die Realisierung von Y erfordert keine besonderen Maßnahmen, sondern kann mit den Mitteln des λ-Kalküls erfolgen (Abschnitt 2.11.3). Beispiel Mit fac′ ≡ λh. λn. cond(= n 0) 1 (∗ n (h (− n 1))) ergibt sich als Berechnung der Fakultät von 1: Y fac′ 1 =β fac′ (Y fac′ ) 1 ≡ (λh. λn. cond (= n 0) 1 (∗ n (h (− n 1)))) (Y fac′ ) 1 →β (λn. cond (= n 0) 1 (∗ n (Y fac′ (− n 1)))) 1 →β cond (= 1 0) 1 (∗ 1 (Y fac′ (− 1 1))) →δ cond false 1 (∗ 1 (Y fac′ (− 1 1))) →δ ∗ 1 (Y fac′ (− 1 1)) →δ ∗ 1 (Y fac′ 0) =β ∗ 1 (fac′ (Y fac′ ) 0) ≡ ∗ 1 ((λh. λn. cond (= n 0) 1 (∗ n (h (− n 1)))) (Y fac′ ) 0) →β ∗ 1 ((λn. cond (= n 0) 1 (∗ n (Y fac′ (− n 1)))) 0) →β ∗ 1 (cond (= 0 0) 1 (∗ 0 (Y fac′ (− 0 1)))) →δ ∗ 1 (cond true 1 (∗ 0 (Y fac′ (− 0 1)))) →δ ∗ 1 1 →δ 1 2.11 Der reine λ-Kalkül Wie in den Abschnitten 2.1 und 2.3 bereits angedeutet wurde, verliert der λ-Kalkül nicht an Mächtigkeit, wenn keine Konstanten zur Verfügung stehen, d.h. die Menge der Konstanten leer ist: C = ∅. Um diese (beweisbare) Behauptung mit Inhalt zu füllen, soll an dieser Stelle exemplarisch untersucht werden, auf welche Art natürliche Zahlen und boolesche Werte mit entsprechender Fallunterscheidung repräsentiert werden können (Abschnitt 2.11.1 und 2.11.2). Wegen seiner konzeptionellen Bedeutung im Zusammenhang mit der Rekursion wird ebenso eine mögliche Realisierung des Y-Kombinators in Abschnitt 2.11.3 betrachtet.
21
2.11 Der reine λ-Kalkül
2 UNTYPISIERTER λ-KALKÜL
2.11.1 Darstellung natürlicher Zahlen Die Church-Numerale beruhen auf dem Ansatz, die Zahl n durch n-malige Anwendung einer Funktion auf ein Argument darzustellen. Die Unabhängigkeit von einer konkreten Funktion f und einem konkreten Argument x wird durch entsprechende Abstraktionen hergestellt: 0 ≡ λf. λx. x 1 ≡ λf. λx. f x 2 ≡ λf. λx. f (f x) 3 ≡ λf. λx. f (f (f x)) .. . n ≡ λf. λx. f (f (f (· · · f x · · · ))) {z } | n-mal
Als kurze Notation wird n ≡ λf. λx. f n x verwendet. Wichtige Funktionen, die auf natürlichen Zahlen operieren, sind die Nachfolgerfunktion sowie die Addition und Multiplikation. Die Nachfolgerfunktion muß die folgende Eigenschaft besitzen: succ n ≡ n + 1 ≡ λf. λx. f n+1 x ≡ λf. λx. f (f n x) =β λf. λx. f ( (λf. λx. f n x) f x) ≡ λf. λx. f (n f x) Das führt auf die Realisierung succ ≡ λn. λf. λx. f (n f x). Die Addition muß die Summe ihrer Argumente berechnen: add n m ≡ n + m ≡ λf. λx. f n+m x ≡ λf. λx. f n (f m x) =β λf. λx. f n ( (λf. λx. f m x) f x) ≡ λf. λx. f n (m f x) =β λf. λx. (λf. λx. f n x) f (m f x) ≡ λf. λx. n f (m f x) Damit lautet die Realisierung add ≡ λn. λm. λf. λx. n f (m f x). Für die Multiplikation führt eine analoge Überlegung der Art mult n m ≡ n · m auf die Form mult ≡ λn. λm. λf. n (m f ).
22
2.11 Der reine λ-Kalkül
2 UNTYPISIERTER λ-KALKÜL
2.11.2 Darstellung boolescher Werte Eine gängige Sicht auf boolesche Werte ist es, sie als eine Art „Entscheidungshilfe“ des Fallunterscheidungsoperators aufzufassen. Eine besonders einfache Repräsentation ergibt sich aber, wenn man die Werte selbst die Fallunterscheidung realisieren läßt. true und false sind dann zweistellige Funktionen (in Curry-Form), wobei true das zweite und false das erste Argument beseitigt und das jeweils andere reproduziert wird. Aus if wird dann ganz schlicht die Identität. false ≡ λx. λy. y true ≡ λx. λy. x if ≡ λx. x Beispiel if true E1 E2 ≡ (λx. x)(λx. λy. x) E1 E2 →β (λx. λy. x) E1 E2 →β (λy. E1 ) E2 →β E1 2.11.3 Realisierung des Y-Kombinators Der Schlüssel zur Realisierung des Fixpunktkombinators Y liegt in der Ausnutzung der Selbstapplikation, die im untypisierten λ-Kalkül problemlos möglich ist. Die Churchsche Version des Fixpunktkombinators lautet: Y ≡ λh. (λx. h (x x))(λx. h (x x)) Es ergibt sich die gewünschte Eigenschaft aus Abschnitt 2.10 für beliebiges f : Yf ≡ (λh. (λx. h (x x))(λx. h (x x))) f →β (λx. f (x x))(λx. f (x x)) →β f ((λx. f (x x))(λx. f (x x))) =β f (Yf )
wegen Yf →β (λx. f (x x))(λx. f (x x))
23
3 TYPISIERTE λ-KALKÜLE
3 Typisierte λ-Kalküle Der λ-Kalkül, wie er bislang betrachtet wurde, kennt keine Einschränkungen beim Aufbau von Termen außer den in Abschnitt 2.1 angegebenen syntaktischen Regeln. Insbesondere wird in einer Kombination E1 E2 durch die Wahl von E1 nicht die Menge der zulässigen Ausdrücke für E2 beeinflußt. Sowohl E1 als auch E2 können beliebige λ-Ausdrücke sein. Die meisten Programmiersprachen hingegen verwenden ein Typisierungskonzept, das die Bildungsmöglichkeiten von Termen einschränkt, so daß ein gegebenes E1 nicht auf völlig beliebige E2 appliziert werden kann. Zum einen muß E1 selbst einen „funktionsartigen“ Typ besitzen, sonst macht die Applikation von vornherein keinen Sinn. Zum anderen kann E2 nur dann als Argument dienen, wenn es von passendem Typ ist, abhängig vom Typ von E1 . Die Typisierung dient also zur Herstellung von Nebenbedingungen für die Termbildung, wobei die Intentionen dahinter vielschichtig sind. Unter Implementierungsgesichtspunkten betrachtet kann häufig eine gesteigerte Effizienz des generierten Codes erreicht werden. Auf höherem Abstraktionsniveau läßt sich Typisierung als Mittel zur partiellen Spezifikation von Algorithmen verstehen. Pragmatisch gesehen trägt sie dazu bei, eine bestimmte Klasse von Programmierfehlern frühzeitig durch (statische) Typprüfung zu erkennen. Auch für den λ-Kalkül existieren Typisierungsmodelle in vielfältigen Versionen, die sich in die Familien der Curry- und Church-Systeme unterteilen lassen, basierend auf den frühen Arbeiten von Curry (1934)15 und Church (1940). Im Folgenden werden nur die elementarsten Aspekte des einfach typisierten λ-Kalküls λ→ betrachtet. Weiterführende Behandlungen finden sich beispielsweise in Hindley & Seldin (1986); Girard et al. (1989); Krivine (1990); Barendregt (1990). 3.1 λ→-Curry Die Grundidee aller Curry-Systeme ist es, Terme des untypisierten λ-Kalküls zu betrachten und Typen im Kontext einer Basismenge von Variablendeklarationen zu inferieren, ausgehend von einer Menge von Typen und einer Menge von Typzuordnungsregeln. Da die Terme selbst keinerlei Typinformationen tragen, handelt es sich somit um eine implizite Typzuordnung. Die Unterschiede zwischen den Curry-Systemen liegen in der Wahl der Mengen von Typen und Typzuordnungsregeln. Viele Varianten lassen sich als Erweiterungen von λ→ auffassen, so daß die Beschäftigung mit λ→ Voraussetzung für das Verständnis dieser komplexeren Systeme ist. Definition Sei V eine Menge von Typvariablen. Die Menge T der Typen des λ→ -Systems ist dann induktiv definiert: Typvariable
α∈V⇒α∈T
Funktionstyp
σ, τ ∈ T ⇒ (σ → τ ) ∈ T 15
Diese Quelle befaßt sich vornehmlich mit kombinatorischer Logik. Die Übertragung auf den λ-Kalkül wurde in Curry & Feys (1958) sowie Curry et al. (1972) vollzogen.
24
3.1 λ→ -Curry
3 TYPISIERTE λ-KALKÜLE
Funktionstypen werden wie üblich als rechtsassoziativ betrachtet, d.h. σ1 → σ2 → σ3 steht für (σ1 → (σ2 → σ3 )), und griechische Buchstaben α, β, γ, . . . dienen als Typvariablen. Um auszudrücken, daß E ∈ Λ vom Typ σ ∈ T ist, wird E : σ geschrieben und als statement mit E als subject bezeichnet.16 Eine Deklaration ist dann ein statement, das als subject eine Termvariable hat und eine Basis ist eine Menge von Deklarationen mit unterschiedlichen Variablen, z.B. Γ = {x : σ1 , y : σ2 , z : σ3 }. Ferner ist domain(Γ) = {x, y, z} die Menge der Variablen in der Basis und Γ ↾ {x, y} = {x : σ1 , y : σ2 } steht für die Restriktion auf eine bestimmte Variablenmenge. Wenn im Kontext einer Basis Γ mit den folgenden Regeln abgeleitet werden kann, daß E : σ gilt, wird dies als Γ ⊢ E : σ notiert. ∅ ⊢ E : σ wird zu ⊢ E : σ abgekürzt. Statt Γ kann auch direkt eine Aufzählung von Deklarationen benutzt werden, z.B. x : σ1 , y : σ2 ⊢ E : σ oder statt Γ ∪ {x : σ} ⊢ E : τ kürzer Γ, x : σ ⊢ E : τ . Definition λ→ -Curry basiert auf drei Typzuordnungsregeln: Γ ⊢ x : σ, wenn (x : σ) ∈ Γ Γ ⊢ E1 : (σ → τ )
Axiom
Γ ⊢ E2 : σ
Γ ⊢ (E1 E2 ) : τ
→-Elimination
Γ, x : σ ⊢ E : τ Γ ⊢ (λx. E) : (σ → τ )
→-Einführung
Die →-Elimination beschreibt die Anwendung einer Funktion auf ein Argument und drückt aus, daß der Typ σ des Arguments zum Urbildbereich der Funktion passen muß. Das Ergebnis ist dann vom Typ τ des Bildbereichs. In ähnlicher Weise entspricht die →-Einführung den intuitiven Erwartungen. Die Deklaration der Variablen x wird herangezogen, um den Urbildbereich der entstehenden Funktion zu beschreiben. Bemerkenswert daran ist jedoch, daß die Deklaration dabei aus dem Kontext entfernt wird, d.h. im Kontext befinden sich immer nur die Deklarationen freier Variablen, die Typen gebundener Variablen sind nur noch implizit in den Typen der Abstraktionen enthalten.17 Diese Beobachtung läßt bereits erahnen, daß für einen typisierbaren Ausdruck E ein enger Zusammenhang besteht zwischen den in E auftretenden Variablen und den Deklarationen der Basis, die zur Inferenz benutzt wurde. 16
Diese Formulierung sollte nicht dahingehend mißverstanden werden, daß der Typ eines Ausdrucks eindeutig bestimmt wäre. In einem statement der Art E : σ ist σ normalerweise als implizit allquantifiziert zu verstehen. Da die oben eingeführte Typmenge keine Konstanten enthält, ist Polymorphie inhärenter Bestandteil des Systems. Im Curry-System λ2 werden die Allquantoren explizit gemacht und die Konstruktionsregeln der Typen verallgemeinert. 17 Eine Variable, die in verschiedenen Abstraktionen gebunden wird, kann jeweils einen anderen Typ besitzen. Wenn gebundene Variablen explizit im Kontext erscheinen würden, hätte dies einen Namenskonflikt im Kontext zur Folge.
25
3.1 λ→ -Curry
3 TYPISIERTE λ-KALKÜLE
Theorem (Basis-Lemma) Seien Γ, Γ′ zwei Basen und Γ′ ⊇ Γ. Dann gilt: Γ ⊢ E : σ ⇒ Γ′ ⊢ E : σ Γ ⊢ E : σ ⇒ F V (E) ⊆ domain(Γ) Γ ⊢ E : σ ⇒ Γ ↾ F V (E) ⊢ E : σ Der erste Teil des Basis-Lemmas sagt aus, daß eine beliebige Vergrößerung einer Basis nicht zur Invalidierung möglicher Ableitungen führen kann, d.h. das Hinzufügen von Deklarationen zu einer Basis ist niemals „kontraproduktiv“. Der zweite Teil beschreibt eine Mindestanforderung an die Basis: Allen freien Variablen des Ausdrucks muß ein Typ zugeordnet werden, d.h. die Typen freier Variablen können nicht auf Umwegen aus ihrer Verwendung erschlossen werden. In Kombination mit dem dritten Teil bedeutet das, daß aber auch nicht mehr als diese Deklarationen benötigt werden. Falls überhaupt in λ→ ein Typ für den Ausdruck abgeleitet werden kann, dann erschließen sich die Typen gebundener Variablen grundsätzlich aus ihrer Verwendung. Beispiel Für beliebige σ, τ ∈ T läßt sich dem Ausdruck (λx. λy. x)(λx. x) der Typ τ → σ → σ zuordnen: x : (σ → σ), y : τ ⊢ x : (σ → σ) x : (σ → σ) ⊢ (λy. x) : (τ → σ → σ)
x:σ⊢x:σ
⊢ (λx. λy. x) : ((σ → σ) → τ → σ → σ)
⊢ (λx. x) : (σ → σ)
⊢ (λx. λy. x)(λx. x) : (τ → σ → σ) Da bei der Bildung von Abstraktionen Variablen dem Kontext entzogen werden, ist es bei komplexeren Termen nicht immer offensichtlich, wie der Startkontext am oberen Ende der Ableitung gewählt werden muß, um unten zum Ergebnis zu gelangen. Es bietet sich daher an, solche Ableitungen technisch von unten nach oben zu entwickeln, also die Regeln rückwärts anzuwenden. Falls dabei Widersprüche auftreten, stellt sich allerdings die Frage, ob nicht eine andere Ableitungskette (von oben nach unten) mit geschickt gewählter Basis möglicherweise zum Erfolg geführt hätte. Die Regeln von λ→ sind allerdings von hinreichend einfacher Gestalt, um sicherzustellen, daß beim rückwärtigen Schließen nicht verzweigt werden muß, um irgendwelche Nebenpfade zu erfassen. Das Generation-Lemma präzisiert diese Vorstellung von der „naheliegenden“ Struktur der Ableitungen. Theorem (Generation-Lemma) Γ ⊢ x : σ ⇒ (x : σ) ∈ Γ Γ ⊢ (E1 E2 ) : τ ⇒ ∃σ ∈ T : Γ ⊢ E1 : (σ → τ ) ∧ Γ ⊢ E2 : σ Γ ⊢ (λx. E) : ρ ⇒ ∃σ, τ ∈ T : Γ, x : σ ⊢ E : τ ∧ ρ ≡ (σ → τ ) Nicht jeder λ-Ausdruck ist in λ→ typisierbar. Insbesondere kann für Terme, die Selbstapplikationen enthalten, in λ→ kein Typ abgeleitet werden.
26
3.1 λ→ -Curry
3 TYPISIERTE λ-KALKÜLE
Beispiel Es gibt kein τ ∈ T und keine Basis Γ, so daß Γ ⊢ (λx. x x) : τ gilt, d.h. der Ausdruck ist in λ→ nicht typisierbar. Wenn er es wäre, müßte die fortgesetzte Anwendung des Generation-Lemmas zu einer konsistenten Ableitung führen, aber es ergibt sich (von unten nach oben konstruiert) für alle Γ: τ1 ≡ (τ3 → τ2 )
τ1 ≡ τ3
Γ, x : τ1 ⊢ x : (τ3 → τ2 )
Γ, x : τ1 ⊢ x : τ3
Γ, x : τ1 ⊢ (x x) : τ2
τ ≡ (τ1 → τ2 )
Γ ⊢ (λx. x x) : τ Es existieren keine Typen τ2 , τ3 ∈ T, für die τ3 ≡ (τ3 → τ2 ) gilt. Wenn ein Term typisierbar ist, dann sind das auch alle Teilterme dieses Terms. Im Umkehrschluß kann der Y-Kombinator in λ→ nicht typisierbar sein, da er auf Selbstapplikation angewiesen ist und diese nicht typisierbar ist. Somit kann keine Rekursion realisiert werden. Das hat den Vorteil, daß die β-Reduktion immer terminiert und in der Folge die β-Konvertibilität entscheidbar wird und den Nachteil, daß sich „fast nichts“ berechnen läßt. Es ist jedoch möglich, den Fixpunktkombinator Yτ : (τ → τ ) → τ für alle τ ∈ T als Konstante zusammen mit der Reduktionsregel Yτ E → E(Yτ E) einzuführen und dadurch Rekursion zu ermöglichen. Wie in der Einleitung zu diesem Abschnitt angedeutet wurde, liegt eine Intention des Typisierungskonzepts in der Vermeidung bestimmter Fehler. Während des Programmablaufs finden fortwährend β-Reduktionen statt. Wenn sich dabei der Typ eines Ausdrucks ändern könnte, wäre es gewiß schwierig, größeren Nutzen aus statischer Typisierung zu ziehen, da trotz Typprüfung Laufzeitfehler möglich wären. Das Subject-Reduction-Theorem schließt solche Probleme aber aus. Theorem (Subject reduction) Wenn E ′ ein β-Redukt des λ-Ausdrucks E ist und E im Kontext Γ typisierbar ist, dann ist auch E ′ im Kontext Γ typisierbar und hat den gleichen Typ: Γ ⊢ E : σ ∧ E →∗β E ′ ⇒ Γ ⊢ E ′ : σ Das gilt jedoch nicht umgekehrt, d.h. die typisierbaren Terme sind unter β-Expansion nicht abgeschlossen. Ein nicht typisierbarer Term kann somit durchaus ein typisierbares Redukt besitzen. Das ist beispielsweise bei (λx. λy. x)(λx. x)(λx. x x) der Fall. Wegen des Bestandteils λx. x x ist der Term nicht typisierbar. Sein Redukt λx. x hat aber den wohldefinierten Typ σ → σ. Es gibt sogar Beispiele E, E ′ ∈ Λ mit E ′ →∗β E, wo beide Ausdrücke typisierbar sind, also ⊢ E : σ und ⊢ E ′ : σ ′ mit geeigneten σ, σ ′ ∈ T gilt, aber 0 E ′ : σ (van Bakel, 1991).18 18
Auf den ersten Blick mag das verblüffend erscheinen, da aus dem Subject-Reduction-Theorem unmittelbar ⊢ E : σ ′ folgt und wegen ⊢ E : σ der Schluß σ ≡ σ ′ naheliegt. Das wäre aber ein Trugschluß, der den polymorphen Charakter des Typsystems übersieht. Es kann durchaus ⊢ E : σ und ⊢ E : σ ′ und σ 6≡ σ ′ gelten, z.B. (λx. λy. y) : (α → β → β) und (λx. λy. y) : ((β → α) → β → β).
27
3.2 λ→ -Church
3 TYPISIERTE λ-KALKÜLE
3.2 λ→-Church Im Gegensatz zur Typisierung nach Curry werden in den Church-Systemen nicht Terme des untypisierten λ-Kalküls nachträglich typisiert, sondern die in den Termen auftretenden gebundenen Variablen werden von vornherein mit Typannotationen versehen. In diesem Sinn handelt es sich um Systeme mit expliziter Typisierung. In λ→ -Church wird die gleiche Menge von Typen T verwendet wie in λ→ -Curry (Abschnitt 3.1). Die Terme unterscheiden sich lediglich durch die Annotationen gebundener Variablen. Statt der Menge Λ der λ-Terme wird daher eine Menge der T-annotierten λ-Terme (Pseudoterme) ΛT zugrunde gelegt: x ∈ V ⇒ x ∈ ΛT Variable E1 , E2 ∈ ΛT ⇒ (E1 E2 ) ∈ ΛT x ∈ V, σ ∈ T, E ∈ ΛT ⇒ (λx : σ. E) ∈ ΛT
Applikation (Kombination) Abstraktion
Beispielsweise wird aus ⊢Curry (λx. x) : (σ → σ) somit (λx : σ. x) : (σ → σ). Die Typzuordnungsregeln sind identisch zu λ→ -Curry bis auf die erforderliche Typannotation in der →-Einführung: Γ, x : σ ⊢ E : τ Γ ⊢ (λx : σ. E) : (σ → τ ) Die Pseudoterme aus ΛT stellen eine Obermenge der korrekt typisierten (legalen) λ→ -Terme dar. Von Interesse sind natürlich nur die legalen Terme: Λ(λ→ ) = {E ∈ ΛT | ∃Γ, σ : Γ ⊢ E : σ} Das Basis-Lemma für λ→ -Church ist identisch zu dem von λ→ -Curry. Das Generation-Lemma überträgt sich entsprechend, wobei für die →-Einführung gilt: Γ ⊢ (λx : σ. E) : ρ ⇒ ∃τ ∈ T : Γ, x : σ ⊢ E : τ ∧ ρ ≡ (σ → τ ) Auf der rechten Seite entfällt also der Existenzquantor für σ, da der Typ von x bereits auf der linken Seite feststeht. Das Subject-Reduction-Theorem gilt auch in λ→ -Church und auch hier sind die legalen Ausdrücke bezüglich β-Expansion nicht abgeschlossen, d.h. es gibt untypisierbare Terme, die typisierbare Redukte besitzen. β-Konvertibilität führt also nicht zur Übertragung der Typisierbarkeitseigenschaft. Allerdings gilt in λ→ -Church — anders als in λ→ -Curry —, daß zwei β-konvertierbare Terme, die beide typisierbar sind, die gleichen Typen haben müssen. Die Typannotation gebundener Variablen erzwingt an dieser Stelle also Eindeutigkeit. Da ein Term immer β-konvertierbar zu sich selbst ist, kann er insbesondere nur einen Typ haben. Theorem (Eindeutigkeit der Typisierung) Γ ⊢ E : σ ∧ Γ ⊢ E : σ′ ⇒ σ ≡ σ′ Γ ⊢ E : σ ∧ Γ ⊢ E ′ : σ ′ ∧ E =β E ′ ⇒ σ ≡ σ ′
28
Literatur
Literatur
Literatur van Bakel, S.J., 1991, Complete restrictions of the intersection type discipline, Theoretical Computer Science 102, 135-163. Barendregt, H.P., 1984, The lambda calculus: its syntax and semantics, revised edition, Studies in Logics and the Foundation of Mathematics, North-Holland. Barendregt, H.P., 1990, Functional programming and lambda calculus, in: van Leeuwen (1990), vol. II, 321-364. Barendregt, H.P., 1991, Lambda Calculi with Types, Vorabdruck aus: Handbook of Logic in Computer Science, vol. II, Oxford University Press. Church, A., 1932/33, A set of postulates for the foundation of logic, Annals of Mathematics (2) 33, 346-366 und 34, 839-864. Church, A., 1932/33, A formulation of the simple theory of types, J. Symbolic Logic 5, 56-68. Church, A., 1941, The calculi of lambda conversion, Princeton University Press. Curry, H.B., 1934, Functionality in combinatory logic, Proc. Nat. Acad. Science USA 20, 584590. Curry, H.B., Feys, R., 1958, Combinatory Logic, Vol. I, Studies in Logic and the Foundations of Mathematics, North Holland. Curry, H.B., Hindley, J.R., Seldin, J.P., 1972, Combinatory Logic, Vol. II, Studies in Logic and the Foundations of Mathematics, North Holland. Field, A.J., Harrison, P.G., 1988, Functional Prgramming, ISBN 0-201-19249-7, Addison-Wesley. Girard, J.-Y., Lafont, Y., Taylor, P., 1989, Proofs and types, Tracts in Theoretical Computer Science 7, Cambridge University Press. Hindley, J.R., Seldin, J.P., 1986, Introduction to Combinators and λ-calculus, London Mathematical Society Student Texts 1, Cambridge University Press. Krivine, J.L., 1990, Lambda-calcul, types et modèles, Masson, Paris. Leeuwen, J. van, 1990, Handbook of Theoretical Computer Science, Elsevier/MIT Press. Kleene, S.C., Rosser, J.B., 1935, The inconsistency of certain formal logics, Annals of Mathematics (2) 36, 630-636. Kleene, S.C., 1936, λ-definability and recursiveness, Duke Math. J. 2, 340-353. Nipkow, T., 1998 Lambda-Kalkül, Vorlesungsskript, URL:http://www4.informatik.tu-muenchen.de/lehre/vorlesungen/lambda/SS98/lambda.ps.gz
Reade, C., 1989, Elements of Functional Programming, ISBN 0-201-12915-9, Addison-Wesley.
29
Literatur
Literatur
Schönfinkel, M., 1924, Über die Bausteine der mathematischen Logik, Math. Ann. 92, 305-316. Stoy, J.E., 1977, Denotational semantics: the Scott-Strachey approach to programming language theory, MIT. Turing, A.M., 1937, Computability and λ-definability, J. Symbolic Logic 2, 153-163.
30