Funktionale Programmierung und Typtheorie Francesco Kriegel TU Dresden Fakultät Mathematik Institut Algebra WS 2008 / 2009 16. April 2010 Inhaltsverzeichnis Kapitel 1 Der λ-Kalkül 1.1 Ungetypter Lambda-Kalkül . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Definition und Syntax. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.2 Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3 Reduktion und Normalform . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.4 Fixpunkte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.5 Curryfizierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.6 primitiv-rekursive und µ-rekursive Funktionen . . . . . . . . 1.1.7 Kombinatorische Logik . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 Getypter Lambda-Kalkül. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Monomorph getypter Lambda-Kalkül λ→ . . . . . . . . . . . . . 1.2.2 Polymorph getypter Lambda-Kalkül λ2 . . . . . . . . . . . . . . . 1.2.3 Polymorph getypter Lambda-Kalkül λ3 . . . . . . . . . . . . . . . 1.2.4 Curry-Howard-Isomorphismus . . . . . . . . . . . . . . . . . . . . . . 3 3 3 6 7 10 11 12 14 16 16 20 21 22 Kapitel 2 Haskell 2.1 Grundprinzipien. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 referentielle Transparenz . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.2 Auswertungsstrategien . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.3 funktionale Programme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.4 Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.5 algebraische Datenstrukturen (abstrakte und rekursive Datentypen) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.6 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.7 Polymorphie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.8 Typklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.9 Typinferenz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.10 Komprehensionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.11 Funktionen höherer Ordnung und partielle Applikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.12 strikte und nicht-strikte Funktionen. . . . . . . . . . . . . . . . . . . 2.1.13 Parser. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 23 23 23 23 24 24 24 24 25 25 27 28 28 28 1 2.1.14 Konstruktorklassen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 Verifikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2.1 Wohlfundierte Induktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3 Transformation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 Fold-Unfold-Methode. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Bird-Meertens-Formalismus . . . . . . . . . . . . . . . . . . . . . . . . . 2.4 Monaden . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 28 29 29 31 31 32 34 1 Der λ-Kalkül 1.1 Ungetypter Lambda-Kalkül 1.1.1 Definition und Syntax Definition 1.1 (λ-Term) Für eine mindestens abzählbar unendliche Menge V von Variablen ist die Menge Λ der λ-Terme über V induktiv definiert durch: (i) x ∈ V =⇒ x ∈ Λ (ii) A ∈ Λ =⇒ (λx.A) ∈ Λ (Abstraktion) (iii) A, B ∈ Λ =⇒ ( AB) ∈ Λ (Applikation) Bemerkung 1.2 Für die Darstellung von λ-Termen benutzen wir folgende Notationen: (a) Die äußersten Klammern können weggelassen werden. (b) Wir schreiben A ≡ B, falls die λ-Terme A und B syntaktisch äquivalent sind. (c) Der Rumpf einer Abstraktion geht so weit wie möglich nach rechts, d.h. λx.( AB) ≡ λx.AB für alle x ∈ V und A, B ∈ Λ. (d) Abstraktion ist rechtsassoziativ und die λ-Bindungen werden zusammengefasst, d.h. λx1 (λx2 (. . . (λxn .A) . . . )) ≡ λx1 .λx2 . . . . λxn .A ≡ λx1 x2 . . . xn .A für alle x1 , x2 , . . . , xn ∈ V und A ∈ Λ. 3 1 Der λ-Kalkül (e) Applikation ist linksassoziativ, d.h. ( AB)C ≡ ABC für alle A, B, C ∈ Λ. Definition 1.3 (freie und gebundene Variable) (a) Für einen λ-Term A ist FV( A) die Menge der freien Variablen, und FV( A) ist induktiv definiert durch (i) FV( x ) = { x } (ii) FV(λx.A) = FV( A) \ { x } (iii) FV( AB) = FV( A) ∪ FV( B) für alle x ∈ V und A, B ∈ Λ. (b) Eine Variable x heißt frei in A, wenn x ∈ FV( A). (c) Ein λ-Term A mit FV( A) = ∅ heißt geschlossener λ-Term oder Kombinator. (d) Λ0 := { A ∈ Λ | FV( A) = ∅} ist die Menge aller Kombinatoren. (e) Entsprechend ist BV( A) die Menge der gebundenen Variablen, und BV( A) ist induktiv definiert durch (i) BV( x ) = ∅ (ii) BV(λx.A) = BV( A) ∪ { x } (iii) BV( AB) = BV( A) ∪ BV( B) Definition 1.4 (Teilterme) Für einen λ-Term A ist Sub( A) die Menge der Teilterme, und diese ist induktiv definiert durch: (i) Sub( x ) = { x } (ii) Sub(λx.B) = {λx.B} ∪ Sub( B) (iii) Sub( BC ) = { BC } ∪ S n S D1 ...Dn = BC i =1 Sub( Di ) Ein λ-Term B heißt Teilterm von A, falls B ∈ Sub( A), und wir schreiben dafür auch B ⊂ A. 4 1.1 Ungetypter Lambda-Kalkül Definition 1.5 (Substitution) Für eine Variable x und λ-Terme A, B ist A[ x := B] die Substitution von x durch B in A, und ist induktiv definiert durch: (i) x [ x := B] ≡ B (ii) y[ x := B] ≡ y (iii) (λx.C )[ x := B] ≡ λx.C (iv) (λy.C )[ x := B] ≡ λy.(C [ x := B]) für x ∈ / Sub(C ) oder y ∈ / FV( B) (v) (λy.C )[ x := B] ≡ λz.(C [y := z][ x := B]) sonst (vi) (CD )[ x := B] ≡ (C [ x := B])( D [ x := B]) Dabei sind die Variablen x, y, z paarweise verschieden und z ∈ / Sub( B) ∪ Sub(C ). Satz 1.6 (Substitutionslemma) Es gilt A[ x := B][y := C ] ≡ A[y := C ][ x := B[y := C ]] für Variablen x, y und λ-Terme A, B, C mit x 6= y und x ∈ / FV(C ). Definition 1.7 (λ-Kalkül) Die Theorie λ hat als Formeln Gleichungen A=B für λ-Terme A, B ∈ Λ. Wir nennen λ auch λ-Kalkül. Es gelten folgende Axiome für alle x, y ∈ V und A, B, C ∈ Λ: (i) A = A (ii) A = B =⇒ B = A (iii) A = B ∧ B = C =⇒ A = C (reflexiv) (symmetrisch) (transitiv) (iv) A = B =⇒ AC = BC ∧ CA = CB (v) A = B =⇒ λx.A = λx.B (ξ ) (vi) λx.A = λy.A[ x := y] für y ∈ / FV( A) (α) (vii) (λx.A) B = A[ x := B] für BV( A) ∩ FV( B) = ∅ ( β) 5 1 Der λ-Kalkül (viii) λx.Ax = A für x ∈ / FV( A) (η ) Die Beweisbarkeit einer Gleichung im λ-Kalkül symbolisieren wir mit λ ` A = B oder einfach A = B, und dann heißen A und B konvertierbar. Es gilt stets A ≡ B =⇒ A = B, aber nicht umgekehrt. 1.1.2 Beispiele B OOLEsche Terme true ≡ λab.a false ≡ λab.b or ≡ λab.atrueb and ≡ λab.abfalse not ≡ λa.afalse true ifthenelse = ite ≡ λabc.abc Satz 1.8 Es gelten ifthenelse trueAB ≡ A und ifthenelse falseAB ≡ B. Paare pair ≡ λpq f . f pq fst ≡ λp.ptrue snd ≡ λp.pfalse Listen cons ≡ pair hd ≡ fst tl ≡ snd nil ≡ λ f .true null ≡ λl.l (λht.false) 6 1.1 Ungetypter Lambda-Kalkül Zahlen Für F, X ∈ Λ und n ∈ N definieren wir die Potenz F n X induktiv durch: (i) F0 X = X (ii) F n+1 X = F ( F n X ) Definition 1.9 (C HURCH-Zahlen) Die C HURCH-Zahlen Cn sind definiert als Cn ≡ λ f x. f n x mit den Operationen: (i) Nulltest iszero ≡ λn.n(λx.false)true (ii) Nachfolgerbildung succ ≡ λn f x. f (n f x ) (iii) Addition add ≡ λmn f x.m f (n f x ) (iv) Multiplikation mult ≡ λmn f .m(n f ) (v) Exponentiation exp ≡ λmn.mn Satz 1.10 Es gelten: (i) iszeroC0 ≡ true und iszero(succCn ) ≡ false (ii) succCn ≡ Cn+1 (iii) addCm Cn ≡ Cm+n und addCm Cn ≡ succm Cn (iv) multCm Cn ≡ Cm·n und multCm Cn ≡ (addCn )m C0 (v) expCm Cn ≡ Cnm und expCm Cn ≡ (multCn )m C1 pred = λn.snd(nh(pairC0 C0 )) h = λp.pair(succ(fstp))(fstp) 1.1.3 Reduktion und Normalform Definition 1.11 (Λ-Hülle, ρ-Reduktion, ρ-Äquivalenz) Sei ρ eine binäre Relation auf Λ. 7 1 Der λ-Kalkül (a) Die Λ-Hülle →ρ von ρ ist eine binäre Relation auf Λ und wird induktiv definiert durch: (i) ( A, B) ∈ ρ =⇒ A →ρ B (ii) A →ρ B =⇒ AC →ρ BC ∧ CA →ρ CB (iii) A →ρ B =⇒ λx.A →ρ λx.B Für A →ρ B sagen wir auch, A ist in einem Schritt ρ-reduzibel zu B, und A heißt ρ-Redex und B heißt ρ-Kontraktum. (b) Die ρ-Reduktion ⇒ρ ist die reflexive, transitive Hülle von →ρ , d.h. es gelten (i) A →ρ B =⇒ A ⇒ρ B (ii) A ⇒ρ A (iii) A ⇒ρ B ∧ B ⇒ρ C =⇒ A ⇒ρ C Für A ⇒ρ B sagen wir auch, A ist ρ-reduzibel zu B. (c) Die ρ-Konvertibilität ist die von →ρ erzeugte Äquivalenzrelation, d.h. die symmetrische, transitive Hülle von ⇒ρ und es gelten: (i) A ⇒ρ B =⇒ A =ρ B (ii) A =ρ B =⇒ B =ρ A (iii) A =ρ B ∧ B =ρ C =⇒ A =ρ C Für A =ρ B sagen wir auch, A und B sind ρ-konvertibel. Wir verwenden obige Definitionen für diese drei Relationen: α := {(λx.A, λy.A[ x := y]) | x, y ∈ V ∧ A ∈ Λ ∧ y ∈ / FV( A)} β := {((λx.A) B, A[ x := B]) | x ∈ V ∧ A, B ∈ Λ ∧ BV( A) ∩ FV( B) = ∅} η := {(λx.Ax, A) | x ∈ V ∧ A ∈ Λ ∧ x ∈ / FV( A)} Definition 1.12 (β-Normalform) (i) Ein λ-Term A ist in β-Normalform, wenn er keine β-Redexe als Teilterme enthält. (ii) Ein λ-Term A hat eine β-Normalform, wenn es einen λ-Term B in β-Normalform mit A = β B gibt. (iii) Ein λ-Term A heißt schwach β-normalisierend, falls eine endliche 8 1.1 Ungetypter Lambda-Kalkül Reduktionsfolge A → β A1 → β A2 → β . . . → β An existiert, sodass An eine β-Normalform ist. (iv) Ein λ-Term A heißt stark β-normalisierend, falls jede Reduktionsfolge A → β A1 → β A2 → β . . . endlich ist. Satz 1.13 (i) Nicht jeder λ-Term hat eine β-Normalform, d.h. nicht alle λ-Terme sind schwach normalisierend. (ii) Es ist nicht entscheidbar, ob ein λ-Term eine β-Normalform besitzt. Beweis: (i) Der λ-Term (λx.xx )(λx.xx ) hat keine β-Normalform. (ii) Beweis über Halteproblem. Lemma 1.14 Falls A ∈ Λ in β-Normalform ist, dann gilt A ⇒ β B =⇒ A = B. Beweis: Für eine β-Normalform A gibt es kein B mit A → β B. Also kann A ⇒ β B nur wegen der Reflexivität von ⇒ β gelten, d.h. A = B. Theorem 1.15 (Erstes C HURCH -R OSSER-Theorem) Seien A, B, C drei λTerme mit A ⇒ β B und A ⇒ β C. Dann gibt es einen λ-Term D mit B ⇒ β D und C ⇒ β D. Folgerung 1.16 (i) Falls A = β B, so existiert ein λ-Term C mit A ⇒ β C und B ⇒ β C. (ii) Falls A eine β-Normalform B hat, dann gilt A ⇒ β B. (iii) Ein λ-Term hat höchstens eine β-Normalform. Definition 1.17 (LO- und LI-Reduktion) (i) Eine LO-Reduktion ist eine Reduktion, bei der der am weitesten links stehende (leftmost ) Redex, der in keinem anderen enthalten ist (outermost ), reduziert wird. Andere Bezeichnungen sind Normal-Order-Reduction und Call-by-Name-Mechanismus. (ii) Eine LI-Reduktion ist eine Reduktion, bei der der am weitesten links stehende (leftmost ) Redex, der keinen anderen enthält (in- 9 1 Der λ-Kalkül nermost ), reduziert wird. Andere Bezeichnungen sind ApplicativeOrder-Reduction und Call-by-Value-Mechanismus. Theorem 1.18 (Zweites C HURCH -R OSSER-Theorem, Normalisierungstheorem) Wenn ein λ-Term A eine β-Normalform B hat, dann existiert eine Folge von LO-Reduktionen von A zu B. 1.1.4 Fixpunkte Theorem 1.19 (i) Jeder λ-Term F besitzt einen Fixpunkt X ∈ Λ, d.h. es gilt FX = X. (ii) Es existiert ein Fixpunktkombinator Y ≡ λ f .(λx. f ( xx ))(λx. f ( xx )), sodass F (YF ) = YF für alle λ-Terme F gilt. Satz 1.20 Die folgenden Terme sind ebenfalls Fixpunktkombinatoren: (i) Yn = λ f .Xnn = λ f . Xn . . . Xn mit Xn = λx1 . . . xn−1 . f ( x1 x1 . . . xn−1 ) | {z } n-mal für n ≥ 2. Speziell ist Y = Y2 . (ii) Y ∗ ≡ λ f .(λgx. f ( gg) x )(λgx. f ( gg) x ) (iii) Yn∗ = λ f .Bnn = λ f . Bn . . . Bn mit Bn = λx1 . . . xn . f ( x1 x1 . . . xn−1 ) xn | {z } n-mal für n ≥ 2. Speziell ist Y ∗ = Y2∗ . (iv) Θ = (λxy.y( xxy))(λxy.y( xxy)) (v) $ ≡ £26 ≡ ££££££££££££££££££££££££££ mit £ ≡ λabcde f ghijklmnopqstuvwxyzr.r (thisisa f ixedpointcombinator ) (vi) Θn = Ann = An . . . An mit An = λx1 . . . xn .xn ( x1 x1 . . . xn ) für n ≥ 2. | {z } n-mal Speziell ist Θ = Θ2 und $ = Θ26 . Mit dem Fixpunkttheorem sind rekursive Definitionen möglich. Möchte man eine Funktion f durch den λ-Term A rekursiv definieren, d.h. f = A und f ∈ FV( A), so erhält man die syntaktisch korrekte Definition vermöge f = Y (λ f .A). 10 1.1 Ungetypter Lambda-Kalkül Beispiel 1.21 (i) Eine intuitive Definition für die Fakultätsfunktion wäre beispielsweise fac = λn.ite(iszeron)C1 (multn(fac(predn))). Die Anwendung des Fixpunktkombinators ergibt fac = Y (λfac.λn.ite(iszeron)C1 (multn(fac(predn)))) (ii) Das Konkatenieren zweier Listen kann rekursiv definiert werden durch append = λxy.ite(nullx )y(cons(hdx )(append(tlx )y)) und syntaktisch korrekt muss die Definition also append = Y (λappend.λxy.ite(nullx )y(cons(hdx )(append(tlx )y))) lauten. (iii) Das Erzeugen einer unendlichen Liste, die nur aus Nullen besteht, kann durch zeroes = consC0 zeroes rekursiv definiert werden. Mit dem Fixpunktkombinator haben wir zeroes = Y (λzeroes.consC0 zeroes). 1.1.5 Curryfizierung Aus der Mathematik ist bekannt, dass für Mengen A, B und C stets die Isomorphie ( A × B) → C ∼ = A → (B → C) gilt, das bedeutet für eine Funktion f : ( A × B) → C, dass es eine Funktion f 0 : A → ( B → C ) mit f 0 ( a) = (b 7→ f ( a, b)) gibt, sodass f ( a, b) = f 0 ( a)(b) gilt. Das funktioniert auch im λ-Kalkül mit den Termen curry2 = λ f xy. f ( x, y) und uncurry2 = λ f p. f (fstp)(sndp) 11 1 Der λ-Kalkül und allgemein für n Variablen curryn = λ f x1 . . . xn . f ( x1 , . . . , xn ) und uncurryn = λ f p. f (fstp) . . . (lstp). Damit lässt sich die λ-Abstraktion generalisieren zu λ( x1 , . . . , xn ).A = uncurryn (λx1 . . . xn .A) und entsprechend ergibt sich die β-Reduktion dann zu (λ( x1 , . . . , xn ).A)( B1 , . . . , Bn ) = A[ x1 := B1 , . . . , xn := Bn ], wobei keine der Variablen x1 , . . . , xn frei in B1 , . . . , Bn vorkommen dürfen. 1.1.6 primitiv-rekursive und µ-rekursive Funktionen Definition 1.22 (numerisch, λ-definierbar) Eine Funktion f : Nn → N heißt numerische Funktion. Eine numerische Funktion f heißt λ-definierbar, falls es einen λ-Term F gibt, sodass für alle x1 , . . . , xn ∈ N C f ( x1 ,...,xn ) = FCx1 . . . Cxn gilt. Definition 1.23 (Grundfunktion, Komposition, primitive Rekursion, primitiv-rek (i) Die Grundfunktionen sind die drei numerischen Funktionen • Projektion ( x1 , . . . , xn ) 7→ xi • Nullfunktion ( x1 , . . . , xn ) 7→ 0 • Nachfolgerfunktion x 7→ x + 1. (ii) Die Komposition f = g[h1 , . . . , hn ] : Nm → N von numerischen Funktionen g : Nn → N und h1 , . . . , hn : Nm → N ist definiert durch f ( x1 , . . . , xm ) := g(h1 ( x1 , . . . , xm ), . . . , hn ( x1 , . . . , xm )). (iii) Die primitive Rekursion f : Nn+1 → N zweier numerischer Funk- 12 1.1 Ungetypter Lambda-Kalkül tionen g : Nn+2 → N und h : Nn → N ist definiert durch f (0, y1 , . . . yn ) := h(y1 , . . . , yn ) f ( x + 1, y1 , . . . yn ) := g( f ( x, y1 , . . . yn ), x, y1 , . . . yn ). (iv) Eine numerische Funktion heißt primitiv-rekursive Funktion, wenn sie durch Kompositionen und primitive Rekursionen aus den Grundfunktionen entsteht. Satz 1.24 Jede primitiv-rekursive Funktion ist λ-definierbar. Insbesondere gelten: (i) Die Grundfunktionen sind λ-definierbar vermöge • Projektion λx1 . . . xn .xi • Nullfunktion λx1 . . . xn .C0 • Nachfolgerfunktion λn f x. f (n f x ). (ii) Falls g und h1 , . . . , hn λ-definierbar sind, dann ist deren Komposition f λ-definierbar als F = λx1 . . . xm .G ( H1 x1 . . . xm ) . . . ( Hn x1 . . . xm ). (iii) Falls g und h λ-definierbar sind, dann ist deren primitive Rekursion f λ-definierbar als F = Y (λFxy1 . . . yn .ite(iszerox )( Hy1 . . . yn )( G ( F (predx )y1 . . . yn )(predx )y1 . . . yn )) Definition 1.25 (µ-Rekursion, µ-rekursive Funktion) (i) Die µ-Rekursion f = µg : Nn → N einer numerischen Funktion g : Nn+1 → N ist definiert durch f ( x1 , . . . , xn ) := min{y | g( x1 , . . . , xn , y) = 0}. (ii) Eine numerische Funktion heißt µ-rekursive Funktion, falls sie durch Kompositionen, primitive Rekursionen und µ-Rekursionen aus den Grundfunktionen entsteht. 13 1 Der λ-Kalkül Satz 1.26 Jede µ-rekursive Funktion ist λ-definierbar. Insbesondere gilt: Falls g λ-definierbar ist, dann ist deren µ-Rekursion f λ-definierbar als F = HC0 H = Y (λH.λyx1 . . . xn .ite(iszero( Gx1 . . . xn y))y( H (succy) x1 . . . xn )). Theorem 1.27 Die Turing-berechenbaren Funktionen entsprechen genau den µ-rekursiven Funktionen. Daher ist jede Turing-berechenbare Funktion auch λ-definierbar. Umgekehrt gilt sogar, dass jede λdefinierbare Funktion auch Turing-berechenbar ist. 1.1.7 Kombinatorische Logik Definition 1.28 (CL-Term) Für eine mindestens abzählbar unendliche Menge V von Variablen ist die Menge CL der CL-Terme über V induktiv definiert durch: (i) x ∈ V =⇒ x ∈ CL (ii) K, S ∈ CL (iii) A, B ∈ CL =⇒ ( AB) ∈ CL (Applikation) Definition 1.29 (schwache Reduktion) Auf den CL-Termen ist eine schwache Reduktion →w definiert durch (i) KAB →w A (ii) SABC →w AC ( BC ) Analog zum λ-Kalkül wird die mehrfache schwache Reduktion durch ⇒w gekennzeichnet. Jeder der in den drei Reduktionsregeln links stehenden CL-Terme heißt schwacher Redex und jeder der rechts stehenden CL-Terme heißt schwaches Redukt. Definition 1.30 (schwache Normalform) Ein CL-Term ist in schwacher Normalform, wenn er keine schwachen Redexe enthält. Bemerkung 1.31 (i) Die Definitionen und Sätze aus dem Abschnitt 1.1.3 gelten entsprechend auch für CL-Terme und die schwache Re- 14 1.1 Ungetypter Lambda-Kalkül duktion. Insbesondere gelten auch das Church-Rosser-Theorem und das Normalisierungstheorem. (ii) Die Reduktion →w wird schwach genannt, weil es CL-Terme in schwacher Normalform gibt, deren transformierter λ-Term nicht in β-Normalform ist. Beispiel: KI = (λxy.x )(λx.x ) Satz 1.32 (i) Transformation von CL-Termen in λ-Terme (·)λ : CL → Λ ( x )λ = x ( AB)λ = ( A)λ ( B)λ (K)λ = λxy.x (S)λ = λxyz.xz(yz) (ii) Transformation von λ-Termen in CL-Terme (·)CL : Λ → CL ( x )CL = x ( AB)CL = ( A)CL ( B)CL (λx.A)CL = λ∗ x.( A)CL λ∗ : V × CL → CL λ∗ x.x = I λ∗ x.A = KA für A 6= x ∗ λ x.AB = S(λ∗ x.A)(λ∗ x.B) Definition 1.33 (CL-Kombinatoren) Die CL-Kombinatoren sind definiert als folgende λ-Terme: • Identität: I = SKK = λx.x • Kanzellator: K = λxy.x • Distributor: S = λxyz.xz(yz) • Kompositor: B = λxyz.x (yz) • Permutator: C = λxyz.xzy 15 1 Der λ-Kalkül Es gelten S(KA)(KB) = K( AB) S(KA)I = A S(KA) B = BAB SA(KB) = CAB 1.2 Getypter Lambda-Kalkül 1.2.1 Monomorph getypter Lambda-Kalkül λ→ implizite Typung (Curry) T := VT | (T→T) Λ := V | (λV.Λ) | (ΛΛ) (λx.A) B → β A[ x := B] Definition 1.34 (Typumgebung) Eine rechts-eindeutige Relation Γ ⊆ V×T heißt Typumgebung. Für ein Element ( x, τ ) einer Typumgebung schreiben wir auch x:τ. Weiter setzen wir dom(Γ) := ΓT = { x ∈ V | ∃τ ∈ T : x:τ ∈ Γ} cod(Γ) := VΓ = {τ ∈ T | ∃ x ∈ V : x:τ ∈ Γ} sowie und für X ⊆ V. 16 Γ, x:τ := Γ ∪ { x:τ } Γ| X := Γ ∩ ( X × T) = { x:τ ∈ Γ | x ∈ X } 1.2 Getypter Lambda-Kalkül Definition 1.35 (Typaussage, Wohltypung) Eine Typaussage ist von der Form Γ ` A:τ für eine Typumgebung Γ, einen λ-Term A und einen Typ τ. Für ∅ ` A:τ schreiben wir einfach ` A:τ. Ein λ-Term A heißt wohlgetypt, falls es eine Typumgebung Γ und einen Typ τ gibt, sodass Γ ` A:τ gilt. In diesem Fall heißt τ eine Wohltypung für A. x:τ ∈ Γ (Axiom) Γ ` x:τ Γ, x:τ ` A:σ (→-Introduktion) Γ ` (λx.A):(τ→σ ) Γ ` A:(τ→σ), Γ ` B:τ (→-Elimination) Γ ` ( AB):σ Satz 1.36 (Basislemma) (i) Γ ` A:τ, Γ ⊆ Γ0 =⇒ Γ0 ` A:τ (ii) Γ ` A:τ =⇒ FV( A) ⊆ dom(Γ) (iii) Γ ` A:τ =⇒ Γ|FV( A) ` A:τ Satz 1.37 (Generierungslemma) (i) Γ ` x:τ =⇒ x:τ ∈ Γ (ii) Γ ` (λx.A):τ =⇒ ∃σ, ρ ∈ T : Γ, x:σ ` A:ρ, τ = (σ→ρ) (iii) Γ ` ( AB):τ =⇒ ∃σ ∈ T : Γ ` A:(σ→τ ), Γ ` B:σ Lemma 1.38 Für einen wohlgetypten λ-Term sind auch alle Teilterme wohlgetypt. Satz 1.39 (Substitutionslemma) (i) Γ, x:τ ` A:σ, y ∈ / FV( A) =⇒ Γ, y:τ ` ( A[ x := y]):σ (ii) Γ ` (λx.A):τ, y ∈ / FV( A) =⇒ Γ ` (λy.( A[ x := y])):τ (iii) Γ ` A:τ =⇒ (Γ[σ := ρ]) ` A:(τ [σ := ρ]) (iv) Γ, x:τ ` A:σ, Γ ` B:τ =⇒ Γ ` ( A[ x := B]):σ 17 1 Der λ-Kalkül Theorem 1.40 (Typerhaltungstheorem) Es gilt Γ ` A:τ, A ⇒ β B =⇒ Γ ` B:τ. Theorem 1.41 (Turing) normalisierend. Alle wohlgetypten λ-Terme sind stark β- Definition 1.42 (A:τ?, A:?, ?:τ) (i) Typprüfungsproblem A:τ?: Sei eine Typumgebung Γ, ein λ-Term A und ein Typ τ gegeben. Gilt Γ ` A:τ? (ii) Typisierbarkeitsproblem A :?: Sei eine Typumgebung Γ und ein λTerm A gegeben. Gibt es einen Typ τ, sodass Γ ` A:τ gilt? (iii) Typbewohntheitsproblem ?:τ: Sei eine Typumgebung Γ und ein Typ τ gegeben. Gibt es einen λ-Term A, sodass Γ ` A:τ gilt? Satz 1.43 Die drei Probleme A:τ?, A:? und ?:τ sind für den monomorph getypten λ-Kalkül λ → entscheidbar. Der Fixpunktkombinator Y = λ f .(λx. f ( xx ))(λx. f ( xx )) aus dem ungetypten λ-Kalkül ist im monomorph getypten λ-Kalkül λ → nicht wohlgetypt. Das liegt daran, dass die Selbstapplikation ( xx ) nicht wohlgetypt ist, denn wäre Γ ` ( xx ):τ, so folgt mit dem Generationslemma die Existenz eines Typs σ mit Γ ` x: (σ → τ ) und Γ ` x:σ. Wegen der Rechtseindeutigkeit einer Typumgebung folgt also σ = (σ→τ ), aber dies ist kein gültiger Typ, weil er nicht endlich ist. Damit wäre nun keine Rekursion im getypten λ-Kalkül möglich, und es wären viele Funktionen nicht definierbar. Als Ausweg führen wir für jeden Typ einen eigenen Fixpunktpunktkombinator als Konstante mit einer sogenannten δ-Konversionsregel ein. Definition 1.44 (Fixpunktkombinator) Für jeden Typ τ definieren wir einen Fixpunktkombinator Yτ :((τ→τ )→τ ) 18 1.2 Getypter Lambda-Kalkül zusammen mit der δ-Konversionsregel Yτ F → δ F (Yτ F ) für beliebige λ-Terme F:(τ→τ ). explizite Typung (Church) T := VT | (T→T) Λ := V | (λV:T.Λ) | (ΛΛ) (λx:τ.A) B → β A[ x := B] x:τ ∈ Γ (Axiom) Γ ` x:τ Γ, x:τ ` A:σ (→-Introduktion) Γ ` (λx:τ.A):(τ→σ ) Γ ` A:(τ→σ), Γ ` B:τ Γ ` ( AB):σ (→-Elimination) Satz 1.45 (Eindeutigkeitslemma) (i) Γ ` A:τ, Γ ` A:σ =⇒ τ = σ (ii) Γ ` A:τ, Γ ` B:σ, A = β B =⇒ τ = σ Definition 1.46 () Die Abbildung | · | : Λexp → Λimp ordnet jedem explizit getypten λTerm einen implizit getypten λ-Term zu, indem die Typinformationen der gebundenen Variablen entfernt werden. | x | := x |(λx:τ.A)| := (λx.| A|) |( AB)| := (| A|| B|) Satz 1.47 (i) Für alle A ∈ Λexp gilt Γ ` A:τ =⇒ Γ ` | A|:τ (ii) Für alle A ∈ Λimp gilt Γ ` A:τ =⇒ ∃ B ∈ Λexp : A = | B|, Γ ` B:τ 19 1 Der λ-Kalkül 1.2.2 Polymorph getypter Lambda-Kalkül λ2 implizite Typung (Curry) T := VT | (T→T) | (∀VT .T) Λ := V | (λV.Λ) | (ΛΛ) (λx.A) B → β A[ x := B] x:τ ∈ Γ (Axiom) Γ ` x:τ Γ, x:τ ` A:σ (→-Introduktion) Γ ` (λx.A):(τ→σ) Γ ` A:(τ→σ ), Γ ` B:τ Γ ` ( AB):σ Γ ` A:τ, σ ∈ / FV(Γ) Γ ` A:(∀σ.τ ) Γ ` A:(∀σ.τ ) Γ ` A:(τ [σ := ρ]) (→-Elimination) (∀-Introduktion) (∀-Elimination) Satz 1.48 Die drei Probleme A:τ?, A:? und ?:τ sind für den polymorph implizit getypten λ-Kalkül zweiter Ordnung λ2 nicht entscheidbar. Satz 1.49 In polymorph getypten λ-Kalkül zweiter Ordnung λ2 sind alle Funktionen λ-definierbar, die in Peano-Arithmetik zweiter Ordnung terminieren. Das sind unter anderem alle primitiv-rekursiven Funktionen. explizite Typung (Church) T := VT | (T→T) | (∀VT .T) Λ := V | (λV:T.Λ) | (ΛΛ) | (ΛVT .Λ) | (ΛT) (λx:τ.A) B → β A[ x := B] (Λτ.A)σ → β A[τ := σ] x:τ ∈ Γ Γ ` x:τ 20 (Axiom) 1.2 Getypter Lambda-Kalkül Γ, x:τ ` A:σ Γ ` (λx:τ.A):(τ→σ ) Γ ` A:(τ→σ), Γ ` B:τ Γ ` ( AB):σ (→-Introduktion) (→-Elimination) Γ ` A:τ, σ ∈ / FV(Γ) (Λσ.A):(∀σ.τ ) (∀-Introduktion) Γ ` A:(∀σ.τ ) Γ ` ( Aρ):(τ [σ := ρ]) (∀-Elimination) Satz 1.50 Die beiden Probleme A:τ? und A:? sind für den polymorph explizit getypten λ-Kalkül zweiter Ordnung λ2 entscheidbar. Das Problem ?:τ ist in λ2 nicht entscheidbar. 1.2.3 Polymorph getypter Lambda-Kalkül λ3 K := ∗ | (K⇒K) T := VT | (T→T) | (∀VT::K.T) | (λVT::K.T) | (TT) Λ := V | (λV:T.Λ) | (ΛΛ) | (ΛVT::K.Λ) | (ΛT) (λx:τ.A) B → β A[ x := B] (Λτ::k.A)σ → β A[τ := σ] (λτ::k.σ)ρ → β σ[τ := ρ] τ::k ∈ Γ (Axiom 1) Γ ` τ::k Γ ` τ::∗, Γ ` σ::∗ (→-∗-Regel) Γ ` (τ→σ )::∗ Γ, σ::k ` τ::∗, σ ∈ / FV(Γ) Γ ` (∀σ::k.τ )::∗ (∀-∗-Regel) Γ ` A:τ, Γ ` σ::∗, τ = β σ (β-∗-Konvertibilität) Γ ` A:σ Γ, τ::k ` σ::l (⇒-Introduktion) Γ ` (λτ::k.σ )::(k⇒l ) Γ ` τ::(k⇒l ), Γ ` σ::k Γ ` (τσ)::l (⇒-Elimination) 21 1 Der λ-Kalkül Γ ` τ::∗, x:τ ∈ Γ (Axiom 2) Γ ` x:τ Γ ` τ::∗, Γ, x:τ ` A:σ (→-Introduktion) Γ ` (λx:τ.A):(τ→σ ) Γ ` A:(τ→σ ), Γ ` B:τ Γ ` ( AB):σ (→-Elimination) Γ, σ::k ` A:τ, σ ∈ / FV(Γ) Γ ` (Λσ::k.A):(∀σ::k.τ ) (∀-Introduktion) Γ ` A:(∀σ::k.τ ), Γ ` ρ::k Γ ` ( Aρ):(τ [σ := ρ]) (∀-Elimination) 1.2.4 Curry-Howard-Isomorphismus Theorem 1.51 (Curry-Howard-Isomorphismus) (i) Γ ` A:τ =⇒ cod(Γ) ` τ (ii) Γ0 ` τ =⇒ ∃ A ∈ Λ : Γ ` A:τ mit Γ = { xτ :τ | τ ∈ Γ0 } 22 2 Haskell 2.1 Grundprinzipien 2.1.1 referentielle Transparenz Haskell ist referentiell transparent, d.h. ein Variable hat an allen Stellen ihres Geltungsbereich denselben Wert. Dies ermöglicht einen mathematischen Umgang mit Programmen, d.h. Programmeigenschaften können recht einfach bewiesen und Programme transformiert werden. 2.1.2 Auswertungsstrategien Call-by-Value: LI-Reduktion, strike Auswertung, eager evaluation, application order reduction Call-by-Name: LO-Reduktion, nicht-strikte Auswertung, normal order reduction Call-by-Need: lazy evaluation; eine nicht-strikte Auswertung, bei der Ausdrücke, die zum Ergebnis beitragen und mehrmals auftreten, nur einmal ausgewertet werden 2.1.3 funktionale Programme Ein funktionales Programm ist eine Folge von Funktionsdefinitionen. f1 x11 . . . x1m1 = A1 .. . fn xn1 . . . xnmn = An Die Auswertung einer Funktionsdefinition ist die Belegung ihrer Variablen durch Werte. f B1 . . . Bn = A[x1:=B1,. . . ,xn:=Bn] Funktionen können auch rekursiv definiert sein. 23 2 Haskell 2.1.4 Typen Haskell ist eine getypte Sprache und verwendet das H INDLEY-M ILNERTypsystem. Es gibt Basistypen und Typkonstruktoren, wie → und ×, die zusammengesetzte Typen erzeugen. Haskell ist stark getypt, d.h. zur Laufzeit können keine Fehler durch Anwendung von Funktionen auf Argumente des falschen Typs entstehen. Haskell ist statisch getypt, d.h. zur Übersetzungszeit sind die Typen aller Ausdrücke bekannt, sie werden automatisch inferiert. 2.1.5 algebraische Datenstrukturen (abstrakte und rekursive Datentypen) Eine algebraische Datenstruktur wird durch einen Typkonstruktor sowie endlich vielen Datenkonstruktoren definiert. Datenstrukturen können auch rekursiv definiert sein. Damit sie endlich sind, müssen jedoch auch nullstellige Datenkonstruktoren vorhanden sein. Zum Beispiel haben Listen den Typkonstruktor [], den nullstelligen Datenkonstruktor [] und zweistelligen Datenkonstruktor :. 2.1.6 Pattern Matching Funktionsdefinitionen von Funktionen auf algebraischen Datenstrukturen können intuitiv durch Pattern Matching geschrieben werden, indem für bestimmte Muster von Argumenten jeweils eine eigene Definition erfolgt, sodass alle möglichen Fälle abgedeckt sind. Geeignete Muster sind stets die verschiedenen Datenkonstruktoren eines Datentyps. Beispielsweise kann die Funktion zur Ermittlung der Länge einer Liste rekursiv definiert werden: length [] = 0 length (h:t) = 1 + (length t) 2.1.7 Polymorphie parametrische Polymorphie: Eine Funktion ist parametrisch polymorph, wenn sie auf verschiedenen Typen gleichartig wirkt. Zum Beispiel die Funktion length. Ad-Hoc-Polymorphie: Eine Funktion ist Ad-Hoc-polymorph, wenn sie überladen ist, d.h. wenn sie auf verschiedenen Typen auch verschiedenartig wirkt. Dies tritt in Typklassen auf. 24 2.1 Grundprinzipien 2.1.8 Typklassen Eine Typklasse ist eine Menge von Typen, die durch die Namen und Typen der auf sie anwendbaren Operationen charakterisiert ist. class <context> <class> <type> where <definitions> Ein Element einer Typklasse heißt Instanz dieser Typklasse. instance <context> <class> <type> where <definitions> Die Namen der Operationen sind überladen, da sie in allen Instanzen der Typklasse gleich bezeichnet sind, aber unterschiedliche Definitionen haben können. Die Operationen sind also Ad-Hoc-polymorph. Die Typklassen sind hierarchisch geordnet. 2.1.9 Typinferenz Sei V eine abzählbar unendliche Menge von Variablen und Θ = n∈N Θn eine Menge von Typkonstruktoren, wobei Θn die Menge der n-stelligen Typkonstruktoren ist. Die Menge Θ0 entspricht der Menge der Basistypen. Die Menge T = Typ(V, Θ) der polymorphen Typen über V und Θ ist definiert durch S T ::= V | ( T→T ) | (ΘT . . . T ) Eine Funktionsdefinition hat die Form f x1 . . . xn = A und dabei sind die xi Variablen oder Datenkonstruktorapplikationen. Anhand dieser Gleichung kann der Typ von f inferiert werden. (i) Analyse der äußeren Struktur: Es gilt f :: a1 -> . . . -> an -> a Falls xi eine Datenkonstruktorapplikation ist, so kann der Typ ai durch eine entsprechende Typkonstruktorapplikation konkretisiert werden. Analog für A und a. Beispielsweise folgt aus xi = [] oder xi = (h:t), dass ai = [b] ein Listentyp ist. (ii) Analyse der inneren Struktur: Für jede Applikation BC im Rumpf A wird eine Typgleichung der Form typ(B) = typ(C) -> typ(BC) 25 2 Haskell aufgestellt. Bereits bekannte Typen werden sofort eingefügt, sonst werden stets neue Variablen verwendet. (iii) Unifikation: Die Menge der Typgleichungen {τi = σi | i ∈ I } wird unifiziert, d.h. es wird mit dem Unifikationsalgorithmus von R OBINSON eine Substitution φ : V → Typ(V, Θ) berechnet, deren homomorphe Fortsetzung φ̂ : Typ(V, Θ) → Typ(V, Θ) die Menge der Typgleichungen löst, d.h. es gilt φ̂(τi ) = φ̂(σi ) für alle i ∈ I. φ̂ heißt auch Unifikator. Die homomorphe Fortsetzung φ̂ ist definiert durch φ̂(τ ) := φ(τ ) für alle Variablen τ ∈ V, φ̂(τ→σ ) := (φ̂(τ )→φ̂(σ )) für Funktionstypen und φ̂(θτ1 . . . τn ) := (θ φ̂(τ1 ) . . . φ̂(τn )) für Strukturtypen. Nach dem Satz von R OBINSON existiert für eine Menge von Typgleichungen stets ein (bis auf Umbenennung von Typvariablen) eindeutiger allgemeinster Unifikator, den man durch die homomorphe Fortsetzung der Lösung des Unifikationsalgorithmus von R OBINSON erhält. Ein Unifikator φ heißt allgemeinster Unifikator, falls es für jeden weiteren Unifikator ψ eine Substitution ρ : V → Typ(V, Θ) mit ψ = ρ̂ ◦ φ 26 2.1 Grundprinzipien gibt. uni f y(τ, σ ) =i f (τ ∈ V ∧ τ * σ ) then (τ 7→ σ ) elsei f (σ ∈ V ∧ σ * τ ) then (σ 7→ τ ) elsei f ((τ ∈ Θ0 ∨ σ ∈ Θ0 ) ∧ τ = σ ) then id elsei f (τ = τ1→τ2 ∧ σ = σ1→σ2 ) then uni f ylist([τ1 , τ2 ], [σ1 , σ2 ], id) elsei f (τ = θτ1 . . . τn ∧ σ = θσ1 . . . σn ) then uni f ylist([τ1 , . . . , τn ], [σ1 , . . . , σn ], id) else f ail uni f ylist([], [], φ) =φ uni f ylist(τ : t, σ : s, φ) =let (ψ = uni f y(φ(τ ), φ(σ ))) in i f (ψ = f ail ) then f ail else uni f ylist(t, s, ψ ◦ φ) 2.1.10 Komprehensionen Eine Mengenkomprehension ist beispielsweise M = {n2 | n ∈ N, n mod 2 = 0} und eine entsprechende Listenkomprehension ist l = [nˆ2|n <- [0..],n ‘mod‘ 2 == 0]. Allgemein sind Listenkomprehensionen von der Form [A|q1,. . . ,qn], wobei die qi (i) Generatoren pattern <- list mit pattern :: a und list :: [a], (ii) Selektoren predicate :: Bool, oder (iii) lokale Bindungen let expression, die in nachfolgenden Generatoren und Selektoren gelten, sein können. Es gelten [A|p <- l] = map (\p -> A) l [A|b] = if b then [A] else [] [A|let e] = let e in [A] [A|q1,q2] = [[A|q2]|q1] 27 2 Haskell 2.1.11 Funktionen höherer Ordnung und partielle Applikation Eine Funktion, die eine andere Funktion als Argument oder Ergebnis hat, heißt Funktion höherer Ordnung. Eine Unterversorgung einer Funktion mit Argumenten heißt auch partielle Applikation. 2.1.12 strikte und nicht-strikte Funktionen Eine nicht-terminierende Berechnung wird mit ⊥ symbolisiert. Eine Funktion f heißt strikt im i-ten Argument, wenn ihre Berechnung genau dann nicht terminiert, wenn die Berechnung des i-ten Elements nicht terminiert, d.h. falls f ( x1 , . . . , xi−1 , ⊥, xi+1 , . . . , xn ) = ⊥ für alle x j gilt. Andernfalls heißt f nicht-strikt im i-ten Argument. f heißt strikt, wenn f strikt in allen Argumenten ist, andernfalls nicht-strikt. 2.1.13 Parser Parser implementieren Erkennungsmechanismen für Sprachen. Sie stellen nach Eingabe eines Textes fest, ob dieser zur Sprache gehört oder nicht. Im Fall der Zugehörigkeit gibt der Parser eine Untergliederung des Textes in Struktureinheiten an. Parser können beispielsweise den Datentyp type Parser tok a = [tok] -> [(a,[tok])] verwenden. 2.1.14 Konstruktorklassen Konstruktorausdrücke sind entweder Konstruktorvariablen, Typkonstruktoren oder Konstruktorapplikationen. Jeder Konstruktorausdruck hat einen Kind. Eine Konstruktorklasse ist eine Menge von Typkonstruktoren gleichen Kinds. Zum Beispiel die Klasse der Monaden oder der Funktoren. class <context> <class> <type> where <definitions> instance <context> <class> <type> where <definitions> 28 2.2 Verifikation 2.2 Verifikation 2.2.1 Wohlfundierte Induktion Definition 2.1 (wohlfundierte Menge) Eine geordnete Menge ( M, ≤) heißt wohlfundiert, falls jede nichtleere Teilmenge von M (mindestens) ein minimales Element besitzt. Theorem 2.2 (wohlfundierte Induktion) Sei ( M, ≤) eine wohlfundierte Menge und P ein Prädikat über M. Dann gilt h∀ x ∈ M : {[∀y < x : P(y)] =⇒ P( x )}i =⇒ [∀ x ∈ M : P( x )]. Beweis: Für alle x ∈ M gelte [∀y < x : P(y)] =⇒ P( x ). Sei X := { x ∈ M | ¬ P( x )}, angenommen es gäbe ein x ∈ M mit ¬ P( x ), d.h. X 6= ∅. Dann existiert wegen der Wohlfundiertheit ein minimales Element x0 von X mit ¬ P( x0 ). Weil x0 ein minimales Element ist, folgt für alle y < x0 stets y ∈ / X, also P(y). Nach Voraussetzung ergibt sich damit aber P( x0 ). Widerspruch! Möchte man also die Gültigkeit eines Prädikats P für alle Elemente einer wohlfundierten Menge ( M, ≤) zeigen, so geht man schrittweise vor: (i) Induktionsanfang: Man zeigt, dass P für alle minimalen Elemente von M gilt. (ii) Induktionsschritt: Unter der Voraussetzung, dass P für alle Elemente y < x gilt, zeigt man dass P auch für x gilt. Beispiel 2.3 Die vollständige Induktion ist eine wohlfundierte Induktion über der wohlfundierten Menge (N, ≤) der natürlichen Zahlen mit der üblichen Ordnung. Definition 2.4 (algebraischer Datentyp) Ein algebraischer Datentyp ist definiert durch Dτ1 . . . τn = C1 σ11 . . . σ1k1 | . . . | Cm σm1 . . . σmkm für m > 0, k i ∈ N und σij ∈ T für i ∈ {0, . . . , m} und j ∈ {0, . . . , k i }. Dabei ist T = T 0 | ( Dτ1 . . . τn ) T0 = V | W | (T0 , . . . , T0 ) | (T0 → T0 ) 29 2 Haskell und W ist die Menge aller konstanten Typen sowie aller algebraischen Datentypen. Die Ci heißen Konstruktoren und haben folgende Eigenschaften: (i) Die Konstruktoren sind disjunkt, d.h. aus Ci xi1 . . . xiki = Cj y j1 . . . y jk j folgt i = j. (ii) Die Konstruktoren sind injektiv, d.h. aus Ci xi1 . . . xiki = Ci yi1 . . . yiki folgt xij = yij . (iii) Die Konstruktoren sind surjektiv, d.h. für alle x ∈ Dτ1 . . . τn gibt es ein i mit x = Ci yi1 . . . yiki . Beispiel 2.5 (i) Die strukturelle Induktion über einen algebraischen Datentyp D ist eine wohlfundierte Induktion über der wohlfundierten Menge (D , ≤). Die minimalen Elemente von (D , ≤) sind die Elemente der Konstruktoren, die D nicht beinhalten. Die Elemente der Konstruktoren, die D beinhalten, sind echt größer als die Elemente von D , aus denen sie konstruiert werden. (ii) Die Listeninduktion ist eine strukturelle Induktion über den algebraischen Datentyp L = Listτ = Empty | Consτ (Listτ ). Das einzige minimale Element ist hier Empty und es gilt L < ConsxL für alle x ∈ τ und L ∈ L. (iii) Die Bauminduktion ist eine strukturelle Induktion über den algebraischen Datentyp T = Treeτ = Leafτ | Nodeτ (Treeτ )(Treeτ ). Die minimalen Elemente sind Leafx für alle x ∈ τ. Weiterhin gilt T1 < NodexT1 T2 und T2 < NodexT1 T2 für alle x ∈ τ und T1 , T2 ∈ T. 30 2.3 Transformation 2.3 Transformation 2.3.1 Fold-Unfold-Methode Definition 2.6 (Fold-Unfold-Methode) (i) Definition: Eine neue Funktionsgleichung wird eingefügt, deren linke Seite keine Spezialisierung einer bereits existierenden Funktionsgleichung sein darf. (ii) Instanz: Eine spezialisierte Funktionsgleichung wird eingefügt, indem in einer bereits existierenden Funktionsgleichung für Variablen Ausdrücke einsetzt. (iii) Unfold: Eine neue Funktionsgleichung wird eingefügt, indem in einer bereits existierenden Funktionsgleichung ein Funktionsaufruf durch den definierenden Ausdruck ersetzt wird. (iv) Fold: Eine neue Funktionsgleichung wird eingefügt, indem in einer bereits existierenden Funktionsgleichung ein Ausdruck durch einen Funktionsaufruf ersetzt wird. (v) Abstraktion: Eine neue Funktionsgleichung wird eingefügt, indem in einer bereits existierenden Funktionsgleichung lokale Definitionen eingeführt werden. (vi) Gesetze: Eine neue Funktionsgleichung wird eingefügt, indem (algebraische) Gesetze auf die rechte Seite einer bereits existierenden Funktionsgleichung angewendet werden. Beispiel 2.7 Für eine Liste ganzer Zahlen soll der Mittelwert berechnet werden. avg l = (sum l) ‘div‘ (length l) Unfold avg l = (foldr (+) 0 l) ‘div‘ (sum (map (const 1) l)) Unfold avg l = (foldr (+) 0 l) ‘div‘ (foldr (+) 0 (map (const 1) l)) Abstraktion avg l = x ‘div‘ y 31 2 Haskell where (x,y) = (foldr (+) 0 l,foldr (+) 0 (map (const 1) l)) Definition accum l = (foldr (+) 0 l,foldr (+) 0 (map (const 1) l)) Gesetze accum l = (foldr (+) 0 l,foldr (⊕) 0 l) where x⊕n = 1+n Gesetze accum l = foldr () (0,0) l where x(m,n) = (x+m,1+n) 2.3.2 Bird-Meertens-Formalismus Theorem 2.8 (Dualitätssatz) (i) Erster Dualitätssatz: foldl f e = foldr f e falls f eine assoziative Operation mit neutralem Element e ist. (ii) Zweiter Dualitätssatz: foldl f e = (foldr (flip f) e).reverse (Beachte: Es gilt flip.flip = id und reverse.reverse = id.) Beispiel 2.9 Wegen id = foldr (:) [] folgt aus dem zweiten Dualitätssatz reverse = id.reverse = (foldr (:) []).reverse = foldl (flip (:)) [] Satz 2.10 Fold-Fusion: f.(foldl g e) = foldl h (f e) für f.g x y = h.f x y und f.(foldr g e) = foldr h (f e) für f.g x y = (flip ((flip h).f)) x y 32 2.3 Transformation Theorem 2.11 (Bird-Meertens-Formalismus) (i) Map-Distributivität: (map f).(map g) = map (f.g) (ii) Scan-Lemma: (map (foldl f e)).inits = scanl f e (map (foldr f e)).tails = scanr f e (iii) Map-Promotion: (map f).concat = concat.(map (map f)) (iv) Fold-Promotion: (foldl n e).concat = (foldl n e).(map (foldl n e)) falls n eine assoziative Operation mit linksneutralem Element e und (foldr o e).concat = (foldr o e).(map (foldr o e)) falls o eine assoziative Operation mit rechtsneutralem Element e ist. (v) Fold-Map-Fusion: (foldl f e).(map g) = foldl (flip ((flip f).g)) e (foldr f e).(map g) = foldr (f.g) e (vi) Fold-Scan-Fusion: (foldl ⊕ 0).(scanl 1) = <<.(foldl ⊗ (0 ⊕ 1,1)) mit x << y = x und (x,y) ⊗ z = (x ⊕ (y z),y z) (vii) Fold-Fold-Fusion (Horner): (foldl ⊕ 0).(map (foldl 1)).tails = foldl ⊗ 1 mit x ⊗ y = (x y) ⊕ 1 falls rechtsdistributiv über ⊕ und 0 linksneutrales Element von ⊕ ist. 33 2 Haskell 2.4 Monaden Definition 2.12 (Monade) Eine Monade ist ein Typkonstruktor M zusammen mit zwei polymorphen Funktionen unit :: a -> M a (∗) :: M a -> (a -> M b) -> M b sodass folgende drei Gleichungen gelten: (i) m ∗ unit = m (ii) (unit a) ∗ f = f a (iii) (m ∗ f) ∗ g = m ∗ (f ~ g) Für einen Ausdruck der Form m ∗ (\a -> n) schreiben wir äquivalent auch let a = m in n 34 2.4 Monaden Satz 2.13 Für die Operation (~) :: (a -> M b) -> (b -> M c) -> a -> M c (f ~ g) a = (f a) ∗ g gelten die folgenden drei Gesetze: (i) f ~ unit = f (ii) unit ~ f = f (iii) (f ~ g) ~ h = f ~ (g ~ h) und es gilt m ∗ f = (id ~ f) m Beispiel 2.14 (i) Identitätsmonade: type M a = a unit a = a a ∗ f = f a f ~ g = g.f map f a = f a join a = a (ii) Listenmonade: type M a = [a] unit a = [a] [] ∗ f = [] (h:t) ∗ f = (f h) ++ (t ∗ f) m ∗ f = concat.map f m f ~ g = concat.(map g).f map = map join = concat 35 2 Haskell (iii) Ausgabemonade: type M a = (a,String) unit a = (a,“ “) m ∗ f = (b,s ++ s’) where { m = (a,s), f a = (b,s’) } (f ~ g) a = (c,s ++ s’) where { f a = (b,s), g b = (c,s’) } map f m = (f a,s) where m = (a,s) join m = (a,s ++ s’) where m = ((a,s’),s) (iv) Zustandsmonade: type M a = State -> (a,State) unit a = \s -> (a,s) m ∗ f = \s -> f a s’ where m s = (a,s’) (f ~ g) a = \s -> g b s’ where f a s = (b,s’) map f m = \s -> (f a,s’) where m s = (a,s’) join m = \s -> m’ s’ where m s = (m’,s’) 36 2.4 Monaden Satz 2.15 Für die Operationen map :: (a -> b) -> M a -> M b map f m = m ∗ (unit.f) und join :: M (M a) -> M a join m = m ∗ id gelten die folgenden sieben Gesetze: (i) map id = id (ii) (map f).(map g) = map (f.g) (iii) (map f).unit = unit.f (iv) (map f).join = join.(map (map f)) (v) join.unit = id (vi) join.(map unit) = id (vii) join.(map join) = join.join Theorem 2.16 Ein Typkonstruktor M ist genau dann eine Monade, wenn es Operationen unit, map und join gibt, die die sieben Gesetze aus dem vorigen Satz erfüllen. Weiterhin gilt x ∗ f = join.map f x 37 2 Haskell 38 Literaturverzeichnis [Bar84] B ARENDREGT, H. P.: The Lambda Calculus: Its Syntax and Semantics (Studies in Logic and the Foundations of Mathematics). Revised Edition. Elsevier Science, 1984. – ISBN 0444875082 [Bar91] B ARENDREGT, H. P.: Lambda Calculi with Types. 1991 39 Literaturverzeichnis 40 Index ?:τ, 18 A :?, 18 A:τ?, 18 CL-Kombinator, 15 CL-Term, 14 Λ-Hülle, 7 α-Konvertibilität, 5 β-Normalform, 8 β-Reduktion, 5 η-Reduktion, 5 λ-Kalkül, 5 λ-Term, 3 geschlossener, 4 λ-definierbar, 12 µ-Rekursion, 13 µ-rekursive Funktion, 13 ρ-Kontraktum, 7 ρ-Konvertibilität, 7 ρ-Redex, 7 ρ-Reduktion, 7 ξ-Regel, 5 äquivalent syntaktisch, 3 C HURCH -R OSSER-Theorem Erstes, 9 Zweites, 10 C HURCH-Zahlen, 7 Abstraktion, 3 algebraischer Datentyp, 29 Applikation, 3 Basislemma, 17 Bird-Meertens-Formalismus, 33 Curry-Howard-Isomorphismus, 22 Dualitätssatz, 32 Eindeutigkeitslemma, 19 Erstes C HURCH -R OSSER-Theorem, 9 Fixpunktkombinator, 18 Fold-Unfold-Methode, 31 freie Variable, 4 gebundene Variable, 4 Generierungslemma, 17 geschlossener λ-Term, 4 Grundfunktion, 12 Kombinator, 4 Komposition, 12 konvertierbar, 5 LI-Reduktion, 9 LO-Reduktion, 9 Monade, 34 Normalisierungstheorem, 10 numerisch, 12 primitiv-rekursive Funktion, 12 primitive Rekursion, 12 Reduktion 41 Index LI-, 9 LO-, 9 schwache Normalform, 14 schwache Reduktion, 14 Substitution, 5 Substitutionslemma, 5, 17 syntaktisch äquivalent, 3 Teilterme, 4 Typaussage, 17 Typbewohntheitsproblem, 18 Typerhaltungstheorem, 18 Typisierbarkeitsproblem, 18 Typprüfungsproblem, 18 Typumgebung, 16 Variable, 3 freie, 4 gebundene, 4 wohlfundierte Induktion, 29 wohlfundierte Menge, 29 Wohltypung, 17 Zweites C HURCH -R OSSER-Theorem, 10 42