• Keine Ergebnisse gefunden

Prototypische Validierung eines parametrischen polymorphen Typsystems f¨ur eine Kernsprache von Haskell

N/A
N/A
Protected

Academic year: 2022

Aktie "Prototypische Validierung eines parametrischen polymorphen Typsystems f¨ur eine Kernsprache von Haskell"

Copied!
38
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Prototypische Validierung eines parametrischen polymorphen Typsystems f¨ ur eine Kernsprache

von Haskell

Frederik Harwath 26. Juni 2008

Bachelorarbeit zur Erlangung des Titels Bachelor of Science/Informatik

am Institut f¨ur Informatik im Fachbereich Informatik und Mathematik der Johann Wolfgang von Goethe Universit¨at, Frankfurt am Main

eingereicht bei

Prof. Dr. Manfred Schmidt-Schauß

Professur f¨ur K¨unstliche Intelligenz und Softwaretechnologie Abgabedatum: 30.06.2008

Frederik Harwath Matrikelnummer: 3144934

Obermark 6 63571 Gelnhausen

(2)

Hiermit versichere ich, dass die vorliegende Arbeit von mir selbst¨andig und ohne Zuhilfenahme anderer Quellen oder Hilfsmittel, als der in der Arbeit angegebenen, verfasst wurde.

Gelnhausen, 30.06.2008

Frederik Harwath

(3)

Inhaltsverzeichnis

1 Einleitung 3

2 Grundlagen 4

2.1 Ungetypterλ-Kalk¨ul . . . 4

2.2 Getypteλ-Kalk¨ule . . . 6

2.2.1 Letrec-Ausdr¨ucke . . . 7

2.2.2 Typinferenz . . . 8

2.2.3 Polymorphie . . . 8

2.3 Haskell . . . 9

2.3.1 Datentyp Deklarationen . . . 10

2.3.2 Funktionsdefinitionen . . . 11

2.3.3 Typklassen . . . 12

2.3.4 Seiteneffekte in Haskell . . . 12

2.3.5 Monaden . . . 13

3 Der Kalk¨ul LP LC 15 3.1 Syntax . . . 15

3.2 Typkonsistenzregeln . . . 15

3.2.1 Einschr¨ankungen f¨ur Typen von Anwendungen . . . 16

3.2.2 Einschr¨ankungen f¨ur Typen von Abstraktionen . . . 16

3.2.3 Einschr¨ankungen f¨ur Typen von Let-Ausdr¨ucken . . . 17

3.2.4 Einschr¨ankungen f¨ur Typen von Case-Ausdr¨ucken . . . . 17

3.2.5 seqund Konstruktoranwendungen . . . 17

3.3 Unabh¨angigkeitseigenschaften . . . 18

3.4 Operationale Semantik . . . 19

3.5 Reduktion und Typen . . . 19

3.6 let-Regeln . . . 20

3.6.1 (llet-in) . . . 20

3.6.2 (llet-e) . . . 20

3.6.3 (lapp) . . . 21

3.7 Rule (lbeta) . . . 21

3.7.1 Typ-Instanziierung vor (lbeta) . . . 21

3.7.2 Anwendung von (lbeta) . . . 21

3.8 Rule (cp) . . . 22

3.9 Rule (case) . . . 22

3.10 Rule (abs) . . . 22

4 Implementierung 23 4.1 Aufbau der Implementierung . . . 23

4.2 Definitions . . . 23

4.3 Substitution . . . 25

4.3.1 Substitutionen mit eingeschr¨anktem Bereich . . . 25

4.3.2 Substitution mit Umbenennung . . . 26

4.4 Typecheck . . . 26

4.4.1 Anwendungen . . . 26

4.4.2 Case . . . 28

4.5 Eval . . . 30

4.5.1 Verteilung von Allquantoren . . . 31

(4)

4.5.2 (cp) . . . 33 4.6 Schnittstellen . . . 33 4.6.1 Benutzung . . . 34

5 Zusammenfassung 35

5.1 Fazit . . . 35

(5)

1 Einleitung

Die vorliegende Ausarbeitung ist Teil meiner Bachelorarbeit, die ich im Som- mersemester 2008 an der Professur f¨ur K¨unstliche Intelligenz und Softwaretech- nologie am Institut f¨ur Informatik der Johann Wolfgang von Goethe Universit¨at Frankfurt am Main absolviert habe.

Ziel der Arbeit war die prototypische Implementierung und experimentelle ¨Uber- pr¨ufung eines Lambda Kalk¨uls LP LC, der in [SSS] beschrieben wird.

Der Kalk¨ul wurde entwickelt, um Beweismethoden derKontextuellen ¨Aquivalenz aus ungetypten Lambda Kalk¨ulen auf Kalk¨ule mit parametrisch polymorphem Typsystem zu ¨ubertragen. Solche Kalk¨ule dienen als Grundlage von funktio- nalen Programmiersprachen, wie beispielsweise Haskell. LP LC modelliert die Reduktionsregeln und das Typsystem von Haskell 98 ohne Typklassen. F¨ur das Typsystem werden zwei Repr¨asentation vorgestellt. Einmal erfolgt die Darstel- lung in der ¨ublicheren Form von Inferenzregeln, die es erlauben, f¨ur einen ge- gebenen Ausdruck einen Typ zu berechnen. Die alternative Pr¨asentation des Typsystems erg¨anzt alle Unterausdr¨ucke eines Ausdrucks um Typmarkierun- gen und formuliert Konsistenzregeln, die diese zu erf¨ullen haben, um zul¨assig zu sein. Die Auswertung der Ausdr¨ucke ist mittels einer Operationalen Small- Step Semantik beschrieben. Dabei ist es notwendig, dass die Typmarkierungen w¨ahrend der Auswertung umgeschrieben werden, so dass die Zul¨assigkeit der Typmarkierungen nicht verletzt wird. Das Ziel dieser Bachelorarbeit bestand darin, zu ¨uberpr¨ufen, ob die Typ¨anderungen, die in [SSS] beschrieben werden, dieser Anforderung gen¨ugen.

Zu diesem Zweck sollte der Kalk¨ul in ein Programm umgesetzt werden. Als Im- plementierungssprache wurde dabei die Funktionale Programmiersprache Has- kell vorgegeben. Verwendet wurde dabei derGlasgow Haskell Compiler[GHC08].

Es sollten Datenstrukturen zur Repr¨asentation der markierten Ausdr¨ucke ent- wickelt werden. Diese Datenstrukturen sollten genutzt werden, um die Konsis- tenzregeln und anschließend die Auswertung inklusive der Typ¨anderungen zu implementieren. Letztendlich sollte die Implementierung getestet werden und optional die Ein- und Ausgabeschnittstellen verbessert werden, so dass die Aus- dr¨ucke beispielsweise direkt aus einer Textdatei eingelesen werden k¨onnen.

Die Ausarbeitung ist wie folgt strukturiert: Der erste Abschnitt bietet einen Uberblick ¨¨ uber grundlegende Begriffe, die im Kontext der Arbeit wichtig sind (λ-Kalk¨ule und Funktionale Programmierung in Haskell).

Daraufhin werden die Definitionen des Kalk¨uls aus [SSS] wiedergegeben. Im n¨achsten Abschnitt wird die Implementierung des Programms beschrieben. Hier- bei werden vor allem die Teile der Implementierung betrachtet, die sich nicht in offensichtlicher Weise aus den Kalk¨ulregeln ergaben. Es wird auch auf Pro- bleme eingegangen, die sich bei der Implementierung zeigten und die Ein- und Ausgabe Schnittstellen des Programms werden beschrieben. Abschließend folgt noch eine bewertende Zusammenfassung der Bachelorarbeit.

(6)

2 Grundlagen

Im folgenden Abschnitt sollen einige Hintergr¨unde der vorliegenden Arbeit vor- gestellt werden.λ-Kalk¨ule sind formale Systeme, deren zugrundeliegendes Prin- zip die funktionale Abstraktion ist. Eingef¨uhrt wurden λ-Kalk¨ule von Alonzo Church1in den sp¨aten 1920ern um grundlegende Fragen der Berechenbarkeit zu untersuchen. Besondere Bedeutung kommt ihnen im Bereich der Semantik von Programmiersprachen zu, da einλ-Kalk¨ul als einfaches Modell einer Program- miersprache gedeutet werden kann, das mathematische Untersuchungen zul¨asst.

Die Darstellung in diesem Abschnitt ist sehr verk¨urzt und stellt λ-Kalk¨ule aus einer Programmiersprachen-Perspektive vor.2

2.1 Ungetypter λ-Kalk¨ ul

Wenn man von dem ungetyptenλ-Kalk¨ul spricht, ist zumeist das im Folgenden sehr knapp beschriebene formale System gemeint. Ein Ausdruck des λ-Kalk¨uls ist von der Form

E ::= x|(λx.E)|(EE))

, wobei x ein beliebiger Bezeichner f¨ur eine Variable ist. Die Ausdr¨ucke der Form (λx.E) korrespondieren zu der Definition einer Funktion in einer Pro- grammiersprache, die Ausdr¨ucke der Form (EE) entsprechen der Funktionsan- wendung auf ein Argument. Eine Variable ist in einem Ausdruck entwederfrei oder durch ein λ gebunden. Ein Ausdruck, der keine freien Variablen enth¨alt, wird auch Kombinator genannt. Die freien Variablen eines Ausdrucks sind in- duktiv definiert:

F V(x) =X F V(λx.E) =F V(E)\ {x}

F V(EE0) =F V(E)∪F V(E0)

Auf diesen Ausdr¨ucken definiert nun eine Regel, die sogenannte β-Reduktion, den Effekt der Anwendung

(λx.E)E0→[E/x]E0

[E/x]E0 ist der Ausdruck, der ausE0 hervorgeht, in dem alle freien vorkommen vonxin E0 durchE ersetzt werden. (λx.E)E0 wird auch alsRedex (von engl.

reducible expression,

”reduzierbarer Ausdruck“) bezeichnet.

DieAuswertung eines Ausdrucks kann nun erfolgen, in dem sukzessive in einem Ausdruck ein Redex gesucht wird undβ-Reduktion auf den Redex angewandt wird, bis ein Wert d.h. eine Normalform erreicht ist. Da ein Ausdruck mehr als einen Redex enthalten kann, ist nicht offensichtlich, in welcher Reihenfolge diese Auswertung erfolgen soll. Der Ausdruck ((λx.(λy.y) x)(λu.(λv.v v) u)) ist beispielsweise selbst ein Redex und enth¨alt zwei weitere Redexe (λy.y) x und (λv.v v)u. Wird immer zuerst der am weitesten links außen liegende Re- dex reduziert, bis eine Abstraktion erreicht ist, die keine Anwendungen mehr

1siehe z.B. [Chu41]

2So lange nichts abweichendes angegeben ist, beruhen die Abschnitte ¨uberλ-Kalk¨ule auf der Darstellung aus [Han04]

(7)

als Unterterme enth¨alt (eine m¨ogliche Normalform), spricht man von einerNor- malordnungsreduktion. Hier w¨urde alsoβ-Reduktion auf den gesamten Ausdruck angewendet werden und den Ausdruck ((λy.y)(λu.(λv.v v) u)) ergeben, dann (λu.(λv.v v)u) und zuletzt (λu.u u). Reduziert man stattdessen, bis ein belie- biger Ausdruck, der keine Anwendung ist, erreicht ist, spricht man von einer call-by-name Reduktion - hier entfiele also der letzte Reduktionsschritt. Eine solche Reduktions-Strategien nennt man auch nicht-strikt. Bei einer strikten Auswertung wird das Argument in einer Anwendung immer ausgewertet. Eine Variante dercall-by-name Auswertung ist diecall-by-need Auswertung, bei der Teilausdr¨ucke bei der Reduktion eines Ausdrucks h¨ochstens einmal ausgewertet werden.

Obwohl derλ-Kalk¨ul ¨uber keine anderen Konstrukte zur Darstellung von Daten und erst recht ¨uber keine Kontrollstrukturen, wie sie aus imperativen Program- miersprachen bekannt sind, verf¨ugt, k¨onnen doch alle Turing-berechenbaren Funktionen in ihm dargestellt werden.

Unbeschr¨ankte Rekursion ist mit Hilfe von Fixpunkt-Kombinatoren verf¨ugbar, die hier aber nicht n¨aher betrachtet werden. Daten wie Zahlen oder Boole- sche Werte werden ebenfalls als Funktionen repr¨asentiert. Ein einfaches Bei- spiel soll dies veranschaulichen und zugleich exemplarisch die Auswertung eines Ausdrucks beschreiben. Das Beispiel zeigt, wie Wahrheitswerte und Funktionen uber diesen dargestellt werden k¨¨ onnen.

T rue:= (λt.(λf.t)) F alse:= (λt.(λf.f)) And:= (λa.(λb.((a b)F alse)))

If := (λp.(λt.(λf.((p t)f))))

Der ¨Ubersichtlichkeit halber sollen diese Namen f¨ur dieλ-Ausdr¨ucke im folgen- den Beispiel so lange wie m¨oglich in offensichtlicher Weise verwendet werden.

Außerdem werden einige Klammern, die nach der obigen Beschreibung der Syn- tax vorhanden sein m¨ussten, eingespart.f a bsoll dabei immer(f a) bbedeuten.

If p t f reduziert also zu t, wenn p gleich T rue ist und zu f, wenn p gleich F alse ist. Nun kann man beispielsweise Ausdr¨ucke, wie If (And T rue F alse)F alse T rue- was, um einen Vergleich zu einer bekannten Programmiersprache zu ziehen, zu dem Java Ausdruckif (true && false){false }else{true}korrespondiert und auch ein entsprechendes Ergebnis liefern sollte - mit der Normalordnungsreduktion so auswerten. 7→d deutet hierbei daraufhin, dass eine der obigen Definitionen eingesetzt wurde, 7→β heißt, dass β-Reduktion angewandt wurde.

(8)

If (And T rue F alse)F alse T rue

7→d (λp.λt.λf.p t f) (And T rue F alse)F alse T rue 7→β (λt.λf.(And T rue F alse)t f)F alse T rue

7→β (λf.(And T rue F alse)F alse f)T rue 7→β (And T rue F alse)F alse T rue 7→d ((λa.λb.a b F alse)T rue F alse)F alse T rue

7→β ((λb.T rue b F alse)F alse)F alse T rue 7→β (T rue F alse F alse)F alse T rue 7→d ((λt.λf.t)F alse F alse)F alse T rue

7→β ((λf.F alse)F alse)F alse T rue 7→β F alse F alse T rue 7→d (λt.λf.f)F alse T rue

7→β (λf.f)T rue 7→β T rue

T rue ist eine Normalform, also endet die Auswertung mit dem zu erwartenden Ergebnis.

Auch wenn diese Kodierungen von aussagenlogischen Funktionen im puren λ- Kalk¨ul aus theoretischer Sicht interessant sind, liegt es bei der Verwendung als Grundlage einer Programmiersprache nahe, den Kalk¨ul um spezielle Datentypen zu erweitern.

2.2 Getypte λ-Kalk¨ ule

In Programmiersprachen sind Typen ein Mittel, um Programme der statischen Untersuchung auf Fehler, beispielsweise durch inkonsistente Verwendung von Funktionen auf Daten, zug¨anglich zu machen. Als Grundlage getypter Program- miersprachen bietet es sich daher auch an, getypte λ-Kalk¨ule zu betrachten.

Die urspr¨ungliche Motivation zur Entwicklung dieser Kalk¨ule entstand nicht aus dem Verlangen, eine Grundlage f¨ur eine Programmiersprache zu erschaffen, sondern war viel mehr darin begr¨undet, dass man versuchte bestimmte Parado- xien des ungetyptenλ-Kalk¨uls zu vermeiden, in dem nicht mehr alle syntaktisch denkbaren Ausdr¨ucke auch als Ausdr¨ucke des Kalk¨uls zul¨assig sind. Betrachtet werden also nur noch Ausdr¨ucke, die einen zul¨assigen Typ haben.

Im einfachsten Fall, demeinfach getyptenλ-Kalk¨ul, ist ein TypT dabei von der Form:

T ::= 0 |T →T

0 bezeichnet einen Grundtypen - in theoretischen Betrachtungen geht man oft- mals nur von der Existenz eines Grundtypen aus.

(9)

Ein Ausdruck hat nun also immer einen bestimmten Typ:

E ::= x::T | (λx::T.E) ::T | (E E) ::T)

Die getypten λ-Ausdr¨ucke sind nun die Elemente der Menge S{Λτ|τ ist ein Typ}, wobei die Λτ wie folgt definiert sind:

x::τ ∈Λτ

(M :: (τ0→τ)N ::τ0)∈Λτ

(λx::α.M ::β)∈Λτ, mit τ =α→β

Auf den getypten Ausdr¨ucken kann man entsprechend dem ungetypten Kalk¨ul β-Reduktion etc. definieren. Nicht zu allen ungetypten Ausdr¨ucken existieren entsprechende getypte Ausdr¨ucke. W¨ahrend die ungetypte Identit¨at λ x.x f¨ur jeden Typ τ eine getypte Entsprechung λx:: τ.x:: τ hat, ist es beispielsweise nicht m¨oglich einen Typ f¨ur die Selbstanwendung λx.xx zu finden. x m¨usste zugleich einen Typ τ, wie auch einen Typ τ →τ0 zugewiesen bekommen und das ist nicht m¨oglich. Ebenso gibt es keinen getypten Fixpunkt Kombinator und somit enth¨alt der einfach getypteλ-Kalk¨ul keine M¨oglichkeit, Rekursion zu rea- lisieren. Damit ist die Ausdrucksst¨arke des Kalk¨uls gegen¨uber dem ungetypten Kalk¨ul stark eingeschr¨ankt.

Getypteλ-Kalk¨ule bilden die Grundlage von Programmiersprachen wie ML oder Haskell. Die Kalk¨ule sind aber gegen¨uber dem einfach getyptenλ-Kalk¨ul deut- lich erweitert.

2.2.1 Letrec-Ausdr¨ucke

Eine wesentliche Erweiterung stellt das Hinzuf¨ugen vonLetrec Ausdr¨ucken zu einem getypten Kalk¨ul dar. EinLetrecstellt nebenλ-Abstraktionen eine weitere Form dar, Variablen in einem Ausdruck zu binden. Syntaktisch kann dies (ohne Typen geschrieben) etwa so aussehen

let v0=e0, ..., vn=en in e

. Der Ausdruck ist so zu verstehen, dass die Variableviineund in allenejan den Ausdruckeigebunden ist, f¨uri, j= 1, . . . , n. Der Kalk¨ul muss um entsprechende Reduktionsregeln erweitert werden. Außer einer M¨oglichkeit, Ausdr¨ucken nicht nur ”lokal“ Namen zu geben h¨alt damit u.a. auch die M¨oglichkeit rekursiver Funktionsdefinitionen wieder Einzug in den Kalk¨ul. Letrec Ausdr¨ucke k¨onnen auch genutzt werden, um dasSharingin Sprachen mitcall-by-need Auswertung zu realisieren.

Weiter oben wurde von einem Grundtypen ausgegangen. In einem anderen Kalk¨ul k¨onnte es zum Beispiel Grundtypen f¨ur Zahlen oder boolesche Werte geben. Zu diesen Grundtypen bedarf es dann noch Regeln, um Werte dieses Typs einzuf¨uhren und zu eliminieren. F¨ur einen Typ Bool kann beispielsweise die Syntax f¨ur Ausdr¨ucke um nullstelligeDatenkonstrukoren bzw. Konstanten T rue::BoolundF alse::Boolerweitert werden. Ausdr¨ucke mit TypN at, der nat¨urliche Zahlen in Peano-Kodierung repr¨asentiert, k¨onnten ¨uber einen null- stelligen Datenkonstruktor N ull :: N at, der f¨ur die Zahl 0 steht, und einen einstelligen Konstruktor Succ:: (N at → N at), der als Argument einen Aus- druck vom Typ N at bekommt und den Nachfolger dieser Zahl repr¨asentiert,

(10)

eingef¨uhrt werden. Die Zwei w¨are in dieser Kodierung also durch den Ausdruck (Succ(Succ N ull) ::N at) ::N atdarstellbar. Neben speziellen Sprachkonstruk- ten zur Eliminierung von bestimmten Grundtypen (z.B. if ... then ... else f¨ur Bool) bietet sich vor allem in Kalk¨ulen, die als Grundlage einer Programmier- sprache dienen sollen, an, eine einheitliches Konstrukt zur Fallunterscheidung in Abh¨angigkeit von der Form eines konstruierten Ausdrucks, einzuf¨uhren. Dazu kann z.B. f¨ur jeden Typτ ein Ausdruck (case e::τ of(C0x0,0 . . . x0,ar(C0))→ e0 :: σ, ...,(Cn xn,0 . . . xn,ar(Cn)) → en :: σ) :: σ eingef¨uhrt werden, mit der Interpretation, dass dercase Ausdruck zu (λxi,0. . . . λxi,ar(Ci).ei)t0 . . . tar(Ci)

reduziert, wenn e von der Form (Ci t0 . . . tar(Ci)) ist, wobei ar(Ci) die Zahl der Argumente des i. Konstruktors angibt und davon ausgegangen wird, dass dienKonstruktoren von τ von 0 bisndurchnummeriert sind.

Neben der endlichen Menge an interpretierten Grundtypen wird f¨ur gew¨ohn- lich noch eine unendliche Menge von uninterpretierten Typen, sogenannten Typvariablen zu den zul¨assigen Typformen hinzugef¨ugt. Die Typvariablen dienen als Platzhalter f¨ur beliebige andere Typen. Ein Typ, der Typvariablen enth¨alt, kann durch eineTypsubstitution, d.h. eine Funktion, die Typvariablen auf Typen abbildet, instanziiert werden, in dem die Typvariablen in dem Typ durch ihr Bild unter der Typsubstitution ersetzt werden.

2.2.2 Typinferenz

Die explizite Angabe eines Typen f¨ur jeden Unterausdruck ist in Programmier- sprachen, die auf getyptenλ-Kalk¨ulen beruhen, normalerweise nicht n¨otig. Als Alternative verwenden Sprachen wie ML und Haskell einen Algorithmus zur Typrekonstruktion. Das Typsystem ist dabei ¨uber Inferenzregeln definiert, die es erlauben aus den bekannten Grundtypen auf die Typen ganzer Ausdr¨ucke zu schließen. Dadurch k¨onnen Ausdr¨ucke, wie im ungetypten Kalk¨ul, ganz oh- ne Typmarkierungen geschrieben werden. Ein Typsystem dieser Art wird f¨ur gew¨ohnlich nach Roger Hindley und Robin Milner, die beide an entsprechenden Algorithmen zur Typberechnung gearbeitet haben, alsHindley-Milner Typsys- tem bezeichnet.

2.2.3 Polymorphie

Nach Christopher Strachey [Str67] werden Funktionen, die mit unterschied- lichen Typen verwendet werden k¨onnen, ebenso wie die Typsysteme, die die Definition solcher Funktionen erlauben, als polymorph bezeichnet.

Er unterschied dabei zwei Formen der Polymorphie. Bei der ad hoc Poly- morphie ist eine Funktion ¨uber mehreren Typen definiert, verh¨alt sich dabei aber f¨ur jeden Typ anders. Programmiersprachen bieten beispielsweise oft die M¨oglichkeit, Funktionen zuuberladen.¨

Bei der parametrischen Polymorphie ist eine Funktion f¨ur unterschiedliche Typen verwendbar, wenn ihre Definition unabh¨angig von den konkret verwen- deten Typen ist. Angenommen man will - die Existenz der obigen Grundtypen vorausgesetzt - eine Funktion definieren, die als erstes Argument eine Funktion bekommt, die sie zwei mal auf ihr zweites Argument anwendet3 . Ohne die M¨oglichkeit polymorpher Funktionsdefinitionen muss f¨ur jeden Typ eine eigene

3Das Beispiel stammt aus [Pie02]

(11)

Funktion definiert werden:

let doubleN at= (λf.λx.f (f x)) :: ((N at→N at)→N at) doubleBool= (λf.λx.f (f x)) :: ((Bool→Bool)→Bool) a=doubleN at g N ull::N at

b=doubleBool f T rue::Bool g=. . .

f =. . . in . . .

Die beiden Funktionen doubleN atunddoubleBool sind gleich bis auf die Typ- markierungen. Die sogenannte Let-Polymorphie, die erstmalig im Typsystem von ML eingef¨uhrt wurde, erlaubt parametrisch polymorphe Funktionsdefini- tionen in let-Ausdr¨ucken:

let double= (λf.λx.f (f x)) :: (∀a.(a−> a)−> a) a=double g N ull::N at

b=double f T rue::Bool g=. . .

f =. . . in . . .

DieLet-Polymorphieerlaubt es, zu einer großen Zahl an ungetypten Ausdr¨ucken entsprechende getypte Ausdr¨ucke zu konstruieren. Außerdem bleibt die Typ- Inferenz entscheidbar. Das ist f¨ur andere polymorph-getypte λ-Kalk¨ule nicht zwingend der Fall.

2.3 Haskell

Um die Umsetzung der skizzierten Konzepte in einer konkreten Programmier- sprache noch zu verdeutlichen soll ein ¨Uberblick ¨uber die Sprache Haskell gege- ben werden. Außerdem ist Haskell die Implementierungssprache des Programms, das im Rahmen dieser Bachelorarbeit entwickelt wurde und daher bietet es sich an, einige Haskell-spezifische Begriffe im folgenden vorzustellen.

Haskell ist eine Programmiersprache, die auf einem λ-Kalk¨ul mit call-by-need Reduktion und parametrisch-polymorphen Typsystem aufbaut. Dieser Kalk¨ul wird alsKernsprache bezeichnet. Haskell bietet dem Programmierer jedoch vie- le notationelle Vereinfachungen gegen¨uber dieser, die in die Kernsprache ¨uber- setzt werden. Wenn hier von Haskell die Rede ist, so ist damit, bis auf eine kleine Ausnahme, die Sprache gemeint, wie sie im Haskell 98 Report [Jon03]

beschrieben ist.

Zur Illustration der Sprache soll ein einfaches Programm dienen, das arith- metische Ausdr¨ucke von der Standard Eingabe des Systems einliest und aus- wertet. Die Ausdr¨ucke beinhalten dabei Zahlenwerte, Addition, Multiplikation und einen Ausdruck, um Variablen an einen Ausdruck zu binden. Ein Has- kell Programm besteht aus Modulen. In dem Beispiel existieren drei Module, Lang.Expressions,Lang.Eval undMain. Die erw¨ahnte Abweichung vom Haskell 98 Sprachstandard beruht nun darin, dass dieser nur einen flachen Namensraum

(12)

f¨ur Module vorsieht, hier aber die gebr¨auchliche Erweiterung um hierarchische Namensr¨aume vorausgesetzt wird. In gr¨oßeren Programmen ist es aus Gr¨unden der ¨Ubersichtlichkeit von Vorteil, die Module in einem hierarchischen Namens- raum anzuordnen. Da von der M¨oglichkeit im Implementierungs-Teil der Ba- chelorarbeit Gebrauch gemacht wurde, sollte auch das Beispiel an dieser Stelle das Prinzip erkl¨aren. Die ModuleLang.Eval undLang.Expressions w¨aren also im Programmverzeichnis in einem gemeinsamen Unterverzeichnis Lang unter- gebracht. Ein Modul besteht aus einer Menge von Gleichungen, die Datentypen und Funktionen definieren.

2.3.1 Datentyp Deklarationen

m o d u l e L a n g . E x p r e s s i o n s w h e r e

d a t a E x p r e s s i o n = Val Int

| P l u s E x p r e s s i o n E x p r e s s i o n

| Mul E x p r e s s i o n E x p r e s s i o n

| Let B i n d i n g E x p r e s s i o n

| Var V a r N a m e

d e r i v i n g ( Show , R e a d ) t y p e V a r N a m e = S t r i n g

d a t a B i n d i n g = V a r N a m e := E x p r e s s i o n d e r i v i n g R e a d

i n s t a n c e S h o w B i n d i n g w h e r e

s h o w ( x := e ) = x ++ " := " ++ s h o w e

Abbildung 1: Haskell Beispiel, Modul Lang.Expressions

In Abbildung 1 ist das ModulLang.Expressions abgebildet. Um die arithmeti- schen Ausdr¨ucke darzustellen, soll ein neuer Datentyp deklariert werden. Dies geschieht in Haskell mit demdataSchl¨usselwort, indem die m¨oglichen Konstruk- toren des Datentyps getrennt durch|mit den jeweiligen Typen ihrer Argumente aufgez¨ahlt werden. Ein Ausdruck des Typs Expression kann also ein Zahlen- wert sein, beispielsweise (Val 1), die Summe zweier andere Ausdr¨ucke, z.B.

(Plus (Val 1) (Val 2))oder ihr Produkt z.B.(Mul (Plus (Val 1) (Val 2)) (Val 2)). Außerdem gibt es Variablen und einen Ausdruck, der eine Variable in einem Unterausdruck bindet:(Let ((Var ”x”) := (Val 10)) (Plus (Var ”x”) (Val 2))). Um eine Bindung zu repr¨asentieren dient hier der DatentypBinding mit dem Infix-Konstruktor:=.

Mit dem Schl¨usselworttype werden Typsynonyme deklariert - ein Variablenna- me soll hier also nichts anderes als ein String sein. Datentypen k¨onnen auch ¨uber Typen parametrisiert werden. Beispielsweise wird in Haskell f¨ur Berechnungen, die ein optionales Ergebnis vom Typaliefern, der DatentypMaybe a = Just a

| Nothing verwendet.

Ein anderes Beispiel ist der Listentyp.[a]ist eine Liste mit Elementen vom Typ a. Die zugeh¨origen Konstruktoren des Listentyps sind[]::(List a)- die leere Lis- te,Nil genannt - und der Infix-Konstruktor(:)::(a→[a]→a) -cons genannt.

x:xs ist eine Liste, die als erstes Elementxund als restliche Elemente die Liste xs hat. Eine Liste mit den Zahlen 1,2,3 vom Typ [Int] kann also konstruiert werden als 1:2:3:[]. Da Listen eine zentrale Datenstruktur in Haskell sind, wird

(13)

auch die abk¨urzende Schreibweise[1,2,3] unterst¨utzt.

Fallunterscheidungen ¨uber Datentypen, die mit data deklariert wurden, sind uber¨ caseAusdr¨ucke m¨oglich.

2.3.2 Funktionsdefinitionen

m o d u l e L a n g . E v a l ( e v a l ) w h e r e i m p o r t L a n g . E x p r e s s i o n s

e v a l :: E x p r e s s i o n - > [ B i n d i n g ] - > Int e v a l ( Val v ) bs = v

e v a l ( P l u s v v ’) bs = ( e v a l v bs ) + ( e v a l v ’ bs ) e v a l ( Mul v v ’) bs = ( e v a l v bs ) * ( e v a l v ’ bs ) e v a l ( Let b e ’) bs =

let bs ’ = b : bs in e v a l e ’ bs ’ e v a l ( Var x ) b o u n d =

c a s e ( l o o k u p B x b o u n d ) of ( J u s t e ) - > e v a l e b o u n d

N o t h i n g - > e r r o r ( " V a r i a b l e n i c h t g e b u n d e n : " ++ s h o w x )

l o o k u p B :: V a r N a m e - > [ B i n d i n g ] - > M a y b e E x p r e s s i o n l o o k u p B v (( v ’ := e ): bs )

| v == v ’ = J u s t e

| o t h e r w i s e = l o o k u p B v bs l o o k u p B _ [] = N o t h i n g

Abbildung 2: Haskell Beispiel, Modul Eval.Eval

Abbildung 2 zeigt das ModulLang.Eval. Typmarkierungen sind in Haskell nur in wenigen Ausnahmef¨allen n¨otig, k¨onnen aber zur Dokumentation an alle Aus- dr¨ucke geschrieben werden. Bei der Definition der Funktioneval wurde hiervon gebrauch gemacht. Die Funktion bekommt also einen Ausdruck vom Typ Ex- pression und eine Liste vom Typ[Binding] und wertet den Ausdruck zu einer Zahl vom TypInt aus. Funktionsdefinitionen k¨onnen auf mehrere Gleichungen verteilt werden. Die Definition der Auswertungsfunktion eval zeigt, wie dabei pattern matching (

”Mustervergleich“) verwendet werden kann, um die Funktion in Abh¨angigkeit von der Gestalt des Ausdrucks zu definieren. Ist der Ausdruck von der Form (Val v) wird einfach die Zahl v zur¨uckgegeben, f¨ur (P lus e e0) und (M ul e e0) werden die Ausdr¨ucke e und e’ rekursiv ausgewertet und die Ergebnisse addiert/multipliziert. F¨ur den Ausdruck (Let b e) wird die Bindung b zu der Liste der gebundenen Variablen bs hinzugef¨ugt und der Ausdruck e ausgewertet. F¨ur eine Variable (V ar x) wird in der Liste der gebundenen Va- riablen nachgesehen, an welchen Ausdruckxgebunden ist und dieser Ausdruck wird dann ausgewertet. Dazu wird die FunktionlookupBverwendet, die rekursiv die Liste durchl¨auft, um die Bindung zu finden. Dabei wird neben der Fallun- terscheidung ¨uber die Struktur der Liste auch noch eine zweite M¨oglichkeit zur Fallunterscheidung bei Funktionsdefinitionen genutzt, sogenannteGuards. Diese erlauben, eine Fallunterscheidung in Abh¨angigkeit von einem booleschen Aus- druck: Ist die gebundene Variable v0 im betrachteten Element der Liste gleich

(14)

zu der gesuchten Variable v (v == v0 = . . .), so gib den gebunden Ausdruck zur¨uck, ansonsten (otherwise . . .) suche im Rest der Liste weiter.

2.3.3 Typklassen

Typklassen4 bieten eine M¨oglichkeit, ¨uberladene Funktionen in Haskell zu de- finieren. Eine Typklasse besteht aus den Typsignaturen mehrerer Funktionen.

Ein Typ kann zu einerInstanz der Typklasse gemacht werden, in dem Imple- mentierungen dieser Funktionen f¨ur den Typen angegeben werden.

Haskell verwendet mehrere vordefinierte Typklassen. Die Show Typklasse ver- eint beispielsweise alle Typena, die als Zeichenkette darstellbar sind, f¨ur die es also eine Funktionshow gibt, die zu einem Ausdruck vom Typ aeinen String erzeugt. Eine Typklassendeklaration f¨ur Show k¨onnte (hier im Vergleich zu der eigentlich Definition in Haskell verk¨urzt) so aussehen:

c l a s s S h o w a w h e r e

s h o w :: a - > S t r i n g

In Abbildung 1 auf Seite 10 ist unten eine Show-Instanz f¨ur denBinding Typ angegeben. F¨ur bestimmte Standard-Typklassen k¨onnen diese Instanzen auto- matisch abgeleitet werden, daf¨ur dienen die deriving . . . Zeilen unterhalb der anderen Datentyp-Deklarationen.

Typklassen verbinden in Haskell ad hoc Polymorphie mit parametrischer Poly- morphie. Sie erlauben eine beschr¨ankte Quantifizierung in polymorphen Typen.

Beispielsweise kann das t im Typ des Ausdrucks (λx → λy → if (x ==

y) then x else y) :: (Eq t) ⇒ (t → t → t) jeder Typ sein, so lange es f¨ur diesen Typen eine Eq Instanz gibt, wobei Eq die Typen enth¨alt, f¨ur die ein Gleichheitstest (==) ::a→a→Booldefiniert ist.

2.3.4 Seiteneffekte in Haskell

Die bisherigen Funktionsdefinitionen waren referentiell Transparent und frei von Seiteneffekten. F¨ur eine bestimmte Eingabe liefern sie immer das gleiche Ergeb- nis und haben keinerlei Auswirkungen auf das System, auf dem sie ausgef¨uhrt werden - sie geben keine Zeichen auf dem Bildschirm aus, lesen keine Zeichen von der Tastatur, ¨ubertragen keine Daten ¨uber ein Netzwerk u.s.w. . Funktio- nen, die neben dem Wert, den sie berechnen, solche Effekte verursachen, ha- ben in Haskell eine gewisse Sonderrolle. F¨ur solche Effekte ist es in der Regel wichtig, dass sie in einer bestimmten Reihenfolge ausgef¨uhrt werden. Da die Auswertung von Ausdr¨ucken in Haskell nicht-strikt erfolgt, ist jedoch keine kla- re Reihenfolge der Auswertung gegeben. Angenommen, es g¨abe eine Funktion printLine::String→(), die als Seiteneffekt Zeichenketten auf dem Bildschirm ausgibt. In einer strikten Sprache w¨urde die Auswertung vonxs = [ printLine

”Hallo”, printLine ”Welt”] die Zeichenkette

”Hallo Welt“ ausgeben. In einer nicht-strikten Sprache k¨onnte durchaus gar keine Ausgabe erfolgen, wenn xs z.B nur in dem Ausdrucklength xs auftritt, weil die Liste dann gar nicht ausge- wertet wird, da die length Funktion die L¨ange einer Liste berechnet, ohne deren Elemente auszuwerten5.

Um also Seiteneffekte in einer nicht-strikten Sprache wie Haskell verwenden zu

4[WB89]

5Das Beispiel ist frei ¨ubernommen aus [Pey01]

(15)

m o d u l e M a i n w h e r e

i m p o r t L a n g . E x p r e s s i o n s i m p o r t E v a l . E v a l

m a i n = e v a l L o o p

e v a l L o o p = do e x p r < - g e t L i n e

p u t S t r L n ( s h o w ( e v a l (( r e a d e x p r ):: E x p r e s s i o n ) [ ] ) ) e v a l L o o p

e v a l L o o p 2 = do e x p r < - g e t L i n e

p u t S t r L n ( s h o w ( e v a l (( r e a d e x p r ):: E x p r e s s i o n ) [ ] ) ) e v a l L o o p

Abbildung 3: Haskell Beispiel, Modul Main

k¨onnen, bedarf es einer M¨oglichkeit, eine Reihenfolge der Auswertung festzule- gen. Abbildung 3 zeigt dasMain Modul des Beispiel Programms. Jedes Haskell Programm verf¨ugt normalerweise ¨uber ein solches Hauptmodul mit einer Funk- tionmain::(IO a). Der IO Typ kann so verstanden werden:type IO a = World

→(a, World). Ein Wert vom TypIO aist also eine Funktion, die einen Zustand der Welt nimmt, eine Aktion ausf¨uhrt und einen Wert vom Typ a, sowie einen neuen Zustand der Welt zur¨uckgibt. Die main Funktion ist die einzige Stelle in einem Haskell Programm, an der normalerweise IO Aktionen ausgef¨uhrt werden k¨onnen. Beispielsweise existieren in Haskell die FunktionenputChar::Char→ IO (), die einen Buchstaben auf dem Bildschirm ausgibt und nur den trivia- len Wert () zur¨uckgibt, undgetChar::IO Char, die einen Buchstaben von der Standard-Eingabe liest. Eine sequentielle Komposition von solchen IO Aktionen wird ¨uber den Kombinator (>>=) ::IO a→(a→IO b)→IO b erreicht. Er nimmt das Ergebnis der ersten IO Aktion und reicht es an die zweite Funktion weiter. Um also einen Buchstaben zu lesen und gleich wieder auszugeben, k¨onnte man schreibengetChar >>=putChar. Außerdem existiert noch ein Kombina- tor (>>) ::IO a→IO b→IO b, der das Ergebnis der ersten Aktion verwirft und ein Kombinator return:: a→IO a, der nur einen Wert zur¨uckgibt, aber keinen Seiteneffekt verursacht.

Mit diesen Kombinatoren ist es m¨oglich, eine Auswertungsreihenfolge f¨ur Ak- tionen festzulegen. Die Funktion evalLoop zeigt, wie mit Hilfe der Kombina- toren f¨ur unser Beispiel eine Funktion definiert werden kann, die jeweils einen ExpressionAusdruck von der Tastatur liest und das Ergebnis der Auswertung ausgibt.evalLoop2 zeigt die gleiche Funktion indo-Notation, die es erlaubt sol- che Aktionssequenzen ¨ubersichtlicher zu schreiben. Diese Notation wird intern in die Kombinatordarstellung ¨ubersetzt.

2.3.5 Monaden

Tats¨achlich gibt es andere F¨alle als die Ein- und Ausgabe, in denen die obigen Kombinatoren n¨utzlich sein k¨onnen, um funktionale Programme zu strukturie- ren. In Haskell existiert daher eine TypklasseMonad, die wie folgt definiert ist

6:

c l a s s M o n a d m w h e r e 6siehe z.B. [Pey01]

(16)

( > >=) :: f o r a l l a b . m a - > ( a - > m b ) - > m b ( > >) :: f o r a l l a b . m a - > m b - > m b

r e t u r n :: a - > m a f a i l :: S t r i n g - > m a

Einen Typ, der eine Instanz dieser Klasse besitzt, so dass die folgenden Glei- chungen erf¨ullt sind

r e t u r n a > >= k == k a m > >= r e t u r n == m

m > >= (\ x - > k x > >= h ) == ( m > >= k ) > >= h

nennt man, angelehnt an ein Konzept aus dem mathematischen Gebiet der Ka- tegorientheorie, eine Monade. Die Funktion fail in der Klasse Monad ist nicht bestandteil des mathematischen Konzepts der Monade. Sie dient zum signa- lisieren von Fehlern. In der IO Monade f¨uhrt f ail dazu, dass eine Exception ausgel¨ost wird.

Eine M onad-Instanz kann beispielsweise f¨ur den weiter oben erw¨ahnten Typ M aybe definiert werden, wobei (>>) durch (>>=) ausgedr¨uckt werden kann und daher nicht angegeben werden muss:

i n s t a n c e M o n a d M a y b e w h e r e J u s t x > >= f = f x N o t h i n g > >= _ = N o t h i n g r e t u r n x = J u s t x f a i l _ = N o t h i n g

Wenn also z.B. in einer mit (>>=) verkn¨upften Sequenz von Anwendungen der obigen lookupB Funktion, ein Lookup N othing ergibt, also fehlschl¨agt, w¨are das Endergebnis der BerechnungN othing.

(17)

3 Der Kalk¨ ul L

P LC

Die folgenden Ausf¨uhrungen geben den, zum Verst¨andnis der Bachelorarbeit notwendigen Teil von [SSS] wieder. Dabei handelt es sich im wesentlichen um eine ¨Ubersetzung der f¨ur diese Arbeit relevanten Teile des Papiers, wobei ich einige K¨urzung vorgenommen habe, wo es mir unangemessen schien den Detail- grad der Ausf¨uhrungen beizubehalten. Insbesondere wurden alle Beschreibun- gen des nicht-deterministischenambOperators nicht in diese Zusammenfassung

¨

ubernommen, da dieser nach ¨Ubereinkunft mit der Professur auch nicht in der Implementierung der Kalk¨ul Regeln ber¨ucksichtigt wurde. Bei LP LC handelt es sich um einen λ-Kalk¨ul mit letrec, case und Konstruktoren, sowie einem parametrisch-polymorphen Typsystem.

3.1 Syntax

Die Syntax der Ausdr¨ucke wird durch die Grammatik in Abbildung 4 beschrie- ben. Die ci sind hierbei die Datenkonstruktoren, Alti ist die zugeh¨orige Case Alternative. Es wird angenommen, dass der Grundtyp Bool mit den 0-¨aren Konstruktoren bzw. Konstanten T rue, F alse existiert, sowie Listen mit der KonstanteN il und dem bin¨aren KonstruktorConsals Daten- und [.] als Typ- konstruktor.

CaseK ist das zum TypkonstruktorK geh¨orige Case-Konstrukt. Dabei m¨ussen die Alternativen zu allen Konstruktoren angegeben werden. Letrec ist ein rekursives Let, wobei jede VariableVi durch das Letrec in allen Ausdr¨uckenEi und in Egebunden wird.

E ::=V |(E E)|λV.E |(seqE E)

| (letrecV1=E1, . . . , Vn=En inE)

| (ci E1. . . Ear(ci))

| (caseK E Alt1. . . Altn) n=|DK| Alti::= ((ci V1. . . Var(ci))→E)

Abbildung 4: Syntax von Ausdr¨ucken

Abbildung 5 zeigt den Aufbau der Typen, wobei X Typvariablen sind und K Typkonstrutoren. Jedem Typ kann ein Allquantor∀X1, . . . , Xn.T vorangestellt werden, der die Variablen Xi in T bindet.

T ::= X | (T →T)|(K T1. . . Tar(K)) Abbildung 5: Syntax der quantor-freien Typen

3.2 Typkonsistenzregeln

[SSS] enth¨alt zwei unterschiedliche, ¨aquivalente Formulierungen des Typsys- tems von LP LC. Die erste dieser beiden Formulierung besteht aus Typinferenz Regeln. Auf diese wird hier nicht eingegangen, da sie keine unmittelbare Relevanz f¨ur diese Arbeit hat.

(18)

Statt dessen soll an dieser Stelle nur die zweite Repr¨asentation des Typsystems vorgestellt werden. Die Ausdr¨ucke und alle Unterausdr¨ucke in dem Kalk¨ul werden hierbei um Typmarkierungen erweitert. Jede Typmarkierung ist g¨ultig, so lange sie bestimmte Typkonsistenzregeln einh¨alt, die im Folgenden vorgestellt werden.

Jede Variable hat einen festen Typ und kommt in einem Ausdruck und allen Unterausdr¨ucken nur mit genau diesem Typ vor. Umbenennungen gebundener Variablen d¨urfen deren Typ nicht ver¨andern.

Beispiel 1 Der Typ der Komposition ist (.):: ∀a, b, c.(b→ c)→(a→b)→ a→c. Eine zul¨assige Typmarkierung w¨are:

(λf :: (b→c)→(λg:: (a1 →b)−>(λx::a2 →(f :: (b→c)(g:: (a1→b)x::

a2) ::b) ::c) :: (∀a2.(a2→c))) :: (∀a1.(((a1→b)→a1)→c))) :: (∀a, b, c.((b→ c)−>(((a→b)→a)→c)))

Eine Typsubstitution ρ ist eine Funktion, die Typvariablen durch Typen er- setzt. Dom(ρ) := {X | ρ(X) 6= X}, Cod(ρ) := {ρ(x) | x ∈ Dom(ρ)} und VCod(ρ) := S

X∈Dom(ρ)FVtype(ρ(X)). Eine Substitution ρ ist frisch, genau dann, wenn Dom(ρ)∩ VCod(ρ) = ∅. Der Typ ∀Y.T0 ist eine Instanz des Typen ∀X.T, genau dann, wenn es ein σ mit Dom(σ) ⊆ X, σT = T0 und Y ⊆ VCod(σ)\FVtype(∀X.T). Nun k¨onnen die eigentlichen Konsistenzregeln formuliert werden.

3.2.1 Einschr¨ankungen f¨ur Typen von Anwendungen

s::∀X.S t::∀Y.T (s t) ::∀Z.R

es gibt eine frische Substitutionρ, so dass gilt Dom(ρ) =X ∪ Y,

Z= VCod(ρ)\FVtype(∀X.S,∀Y.T) ρ(S) =S1→R, ρ(T) =S1

Bei gegebenen Typmarkierungen f¨urs,tund (s t) ist die frische Substitutionρ eindeutig festgelegt.7

3.2.2 Einschr¨ankungen f¨ur Typen von Abstraktionen

Eine Abstraktion (λx.s) muss wie folgt getypt sein λ(x:: S1).(s::∀X.S3) ::

∀Y.S1→S2, wobei

• der TypS1 vonxquantor-frei sein muss,

• eine Substitutionρmit Dom(ρ) =X undρ(S3) =S2existieren muss.

• Y = FVtype(S1 → S2) \ Z gelten muss, mit Z :=

S

z::Tz∈FV(λx.s)(FVtype(Tz)).

Grunds¨atzlich k¨onnen Typvariablen in FVtype(∀Y.S1 → S2) auch durch einen Quantor in einem ¨ubergeordneten Ausdruck gebunden sein.

F¨ur eine geschlossene Abstraktion k¨onnte ein allgemeinerer Typ erzwungen wer- den, in dem verlangt wird, dassY =FVtype(S1→S2).

7Diese Regel wurde gegen¨uber der Formulierung hier durch ein Ergebnis der Tests bei der Implementierung leicht abge¨andert. Siehe hierzu Abschnitt 4.4.1 auf Seite 26.

(19)

Die Monomorpie Einschr¨ankung des Let Polymorphismus ist eine Kombination der obigen Einschr¨ankungen f¨ur den Typ vonxund der Behandlung des∀-Typs und des Argumentestin der Anwendung (s t).

3.2.3 Einschr¨ankungen f¨ur Typen von Let-Ausdr¨ucken

F¨ur die Typmarkierung eines Let-Ausdrucks (letrec Env in s :: ∀X.T1) ::

(∀Y.T2) m¨ussen die folgenden Bedingungen erf¨ullt sein:

1. Es existiert eine injektive, frische Substitution ρ:Y → X, die Typvaria- blen nur durch Typvariablen ersetzt, so dass Dom(ρ) =Y,ρ(T2) =T1und somit auchρY ⊆ X. Bei einer korrekten Typmarkierung kann auch stets ρY =X erreicht werden.

2. In jeder Bindung x=rist der Typ von xeine Instanz des Typen vonr.

3.2.4 Einschr¨ankungen f¨ur Typen von Case-Ausdr¨ucken

Um die Typeinschr¨ankungen f¨ur Case Ausdr¨ucke zu beschreiben, bietet es sich an, die Case Alternativen als Abstraktionen (cK,i xi,1. . . xi,mi)→ti zu schrei- ben.cK,i:λxi,1, . . . , xi,mi.ti.

Eincase-Ausdruck

(caseK (s::∀Z.Ts) ofalt1::∀U1.S1, . . . ,altk ::∀Uk.Sk) ::∀Y.T unterliegt den folgenden Typeinschr¨ankungen:

1. Es muss eine frische Substitutionµmit Dom(µ) =Zexistieren, so dass gilt µ(Ts) = (K Ts,1. . . Ts,k), mit k=ar(K). Seiρ= [Ts,1/X1, . . . , Ts,k/Xk] wobei dieX1, . . . , Xk die Typ Variablen aller Datenkonstruktoren vonK sind.

2. Jede Alternative (cK,i xi,1. . . xi,mi) → ti unterliegt den gleichen Ein- schr¨ankungen, wie die Abstraktionλxi,1, . . . , xi,mi.ti, wobei die xi,j ent- sprechende Typenρ(TK,i,j) haben, d.h. der Typ voncK,i an dieser Stelle ist eine Instanz des Konstruktors instanziiert mitρ.

3. F¨ur jede Alternative des Case-Ausdrucks muss der Typ der Anwendung (λxi,1, . . . , xi,mi.ti)y1. . . yn) ::∀Y.T die Einschr¨ankungen f¨ur Anwendun- gen erf¨ullen, wobeiyj ::∀Z0.ρ(TK,i,j) undZ0= VCod(ρ)\ Y.

Die Einschr¨ankung auf monomorphen Gebrauch von gebundenen Variablen in Abstraktionen durch den Let-Polymorphismus gilt auch f¨ur die Alternativen in Case Ausdr¨ucken.

3.2.5 seqund Konstruktoranwendungen

Andere Arten von vordefinierten Konstrukten k¨onnen wie Konstanten behandelt werden, z.B. kann seq als Konstruktor vom Typ ∀a.a → a → a betrachtet werden.

(20)

F¨ur den konstruierten Ausdruck (cK,i s1. . . smi) kann eine sinnvoller Typ mit Hilfe der obigen Konsistenzregeln wie folgt bestimmt werden, in dem der Ausdruck wie eine verschachtelte Anwendung behandelt wird: Der Typ der Konstruktorinstanz cK,i muss eine Instanz des Konstruktortyps sein, also ein ρ(TK,i,1 → . . . → TK,i,mi → TK,i,mi+1), f¨ur eine Substitution ρ, so dass Dom(ρ) = {X1, . . . , Xn}. Die Typen in ρ(TK,i,j) m¨ussen selbst Instanzen der entsprechenden Typen vonsj sein.

Definition 1 Wenn ein Ausdruck t :: T alle obigen Typ-Einschr¨ankungen erf¨ullt, heißt die Typmarkierung zul¨assig (engl.admissible) und der Ausdruck wohl-getypt (engl.well-typed).

Es kann mehr als eine zul¨assige Typmarkierung f¨ur einen Ausdruck geben.

Definition 2 Eine Typ-Instanz eines Ausdrucks t : ∀X.T mit einer frischen Typ-Substitution ρ mit Dom(ρ)⊆ X wird wie folgt konstruiert: Die Typen der Unterausdr¨ucke von t werden entsprechend instanziiert. Dies kann von oben herab geschehen, wobei m¨oglicherweise Variablen durch entsprechende frische Variablen des instanziierten Typen ersetzt werden m¨ussen.

Definition 3 EineTyp Instanz einer Anwendung ((λx.s)t)mit einer Abstrak- tion als Teilausdruck ist ebenfalls definiert, wobei der Unterausdruck (λx.s) in eingeschr¨ankter Weise instanziiert werden kann, so dass nur Instanziierungen erlaubt sind, die den Typ der Anwendung nicht ¨andern. Strenger formuliert, un- ter Verwendung der Notation aus den Einschr¨ankungen f¨ur Anwendungstypen, wobei (λx.s) ::∀X.S die Abstraktion ist und ρ die Substitution aus der Regel, bedeutet dies, dass nur die Substitutionρ0 :=ρ|X verwendet wird.

Eine uneingeschr¨ankte Instanziierung von Unterausdr¨ucken kann zu nicht zul¨assigen Typmarkierungen f¨uhren.

3.3 Unabh¨ angigkeitseigenschaften

Bei der Reduktion spielen noch zwei weitere Eigenschaften von Typmarkierun- gen eine wichtige Rolle. Das Typsystem erlaubt, dass ein Allquantor in bestimm- ten Situationen verschoben werden kann. Beispielsweise kann der Typ eines Tu- pels (s1::a, s2::a) :: (∀a.(a, a)) generalisiert werden, in dem der Allquantor in den Typ vons1 unds2verschoben wird.

Der Satz h¨alt dies f¨ur Datenkonstruktoren im allgemeinen fest, der n¨achste Satz beinhaltet eine entsprechende Aussage f¨urLetrecAusdr¨ucke.

Satz 4 Sei (c t1 . . . tn) ein Ausdruck vom Typ ∀X.(K S1. . . Sk) und c ein Konstruktor vom Typ∀Y.R1→. . .→Rn→(K Y1. . . Yk)mitY ={Y1. . . Yk}.

Dann k¨onnen im Ausdruck (c t1 . . . tn) die ti unabh¨angig getypt werden: mit µ = [S1/Y1, . . . , Sk/Yk] ergibt sich t1 :: ∀X1.T1, . . . , tn :: ∀Xn.Tn, wobei Ti = ρiµ(Ri) und ρi Umbenennungen der VariablenX mit frischen Variablen sind, so dassρi({X1, . . . , Xn}) =Xi.

Satz 5 Sei (letrec x1 = s1, . . . , xn = sn in s) ein Ausdruck vom Typ

∀X1, . . . Xm.T und sei ∀Yi.Ti vom Typ si, f¨ur i = 1, . . . , n. Dann k¨onnen die

(21)

Typen si, i = 1, . . . , n verallgemeinert werden, in dem die Xi wie folgt umbe- nannt und der∀-Quantor verschoben wird: Die neuen Typen Ti0 von xi,si und s sind durch eine Substitution ρi ={X1 7→ Xi,1, . . . , xm 7→Xi,m} umbenann- te Kopien, wobei Xi,j frische Variablen sind und der ∀-Quantor verteilt wird:

Der Typ von si ist also letztendlich ∀X1,i, . . . Xn,i,Yi.Ti0 und das ∀X1, . . . Xm- Pr¨afix kann auch dem Typen von s vorangestellt werden. Der Typ des Letrec Ausdrucks selbst ¨andert sich nicht.

3.4 Operationale Semantik

(s t)S∨T → (sS t)V

(letrec Env int)T → (letrecEnv intS)V

(letrec x=s,Env inC[xS]) → (letrecx=sS,Env inC[xV]) (letrec x=s, y=C[xS],Env inr) → (letrecx=sS, y=C[xV],Env inr)

wennC[x]6=x

(seqs t)S∨T → (seqsS t)V

(case s alts)S∨T → (casesS alts)V S∨T bedeutet MarkierungS oderT.

Abbildung 6: Redex-Suche mit Markierungen

Der call-by-need Kalk¨ulLP LChat die folgenden Primitiven: Bin¨are Anwendung, Rekursives let, lambda, Konstruktoren case, seqund ambmit einer Normal- ordnungsreduktion, die Auswertung bis auf eine Schwache Kopf-Normalform (WHNF, von engl. Weak Head Normal Form) definiert. Der Kalk¨ul wird fast ungetypt beschrieben und Typen werden nur erw¨ahnt, wo es notwendig ist.

LP LC ist nicht-deterministisch.

Zur Suche des Normalordnungsredex werden MarkierungenS, T verwendet, wo- beiT den ¨außeren Ausdruck (engl.Top Term),S eine Reduktion in einem Un- terausdruck (engl.Subterm Reduction) undV einen bereits durch die Reduktion besuchten Ausdruck (engl.visited) bezeichnet.

Der Markierungsalgorithmus in Abbildung ?? wendet die gegebenen Regeln ersch¨opfend an. Er schl¨agt fehl, wenn eine Schleife festgestellt wird (wenn ein zu besuchender Ausdruck bereits mit einem V markiert ist) und ist erfolg- reich, wenn keine weiteren Regeln mehr angewendet werden k¨onnen. Wenn der Markierungs-Algorithmus auf Kontexte angewendet wird, dann werden die Kon- texte, in denen das Loch mit S, T oder V markiert ist, Reduktionskontexte genannt.

In Abbildung 7 auf der n¨achsten Seite ist ein CV-Ausdruck ein Ausdruck der Form (c x1. . . xn), so dasscein Konstruktor ist und diexi Variablen. EinWert ist eine Abstraktion oder ein Konstruktor-Ausdruck (c t1. . . tn).

Die Regeln der Normalordnungsreduktion sind f¨ur Ausdrucke ohne Typen in Ab- bildung 7 auf der n¨achsten Seite dargestellt, wobei angenommen wird, dass der Markierungsalgorithmus zuvor angewandt wurde. Die ¨Ubertragung der Typisie- rung auf das Ergebnis ist in den meisten F¨allen offensichtlich. Die Sonderf¨alle werden im n¨achsten Abschnitt behandelt.

(22)

(lbeta) C[((λx.s)S r)]→C[(letrecx=rins)]

(cp) (letrecx=vS inC[xV])→(letrecx=v inC[v])

wobeiv eine Abstraktion, Variable oder ein CV-Ausdruck ist (abs) (letrecx= (c t1. . . tn)S,Env inr)→

(letrecx1=t1, . . . , xn=tn, x= (c x1. . . xn),Env inr) nur wenn (c t1. . . tn) kein CV-Ausdruck ist.xisind frische Variablen.

(case) C[(case (c t1. . . tn)S. . .((c y1. . . yn)→s). . .)]

→C[(letrecy1=t1, . . . , yn =tn ins)]

(seq) C[(seqvS t)]→C[t] wennv ein Wert ist (llet-e) (letrecEnv1, x= (letrecEnv2 ins)S int)

→(letrecEnv1,Env2, x=sint) (llet-in) (letrecEnv1 in(letrecEnv2ins)S)

→(letrecEnv1,Env2 ins)

(lapp) C[((letrecEnv ins)S t)]→C[((letrecEnv in(s t))]

(lseq) C[(seq(letrecEnv ins)S t)]→C[(letrecEnv in(seqs t))]

(lcase) C[(case (letrecEnv int)S alts)]

→C[(letrecEnv in(caset alts))]

Abbildung 7: Normalordnungsregeln

Eine Schwache Kopf-Normalform ist ein Wert v oder ein Ausdruck (letrecEnv inv), wobeiv ein Wert ist.

3.5 Reduktion und Typen

Die Reduktion beh¨alt den Typ eines Ausdrucks bei oder verallgemeinert diesen und somit k¨onnen wohl-getypte Ausdr¨ucke keine dynamischen Typfehler erzeu- gen.

Dazu darf der Typ eines Redex sich nicht ver¨andern oder muss verallgemei- nert werden und die Bindungs Struktur eines Typs muss intakt bleiben, d.h. es d¨urfen keine Typvariablen eingefangen werden.

3.6 let-Regeln

Die Unabh¨angigkeitseigenschaften erlauben es, davon auszugehen, dass in ei- nem Ausdruck (letrec Env in s) der Typ von s und der Typ des gesamten Ausdrucks gleich sind.

3.6.1 (llet-in)

(letrecEnv1 in(letrecEnv2 ins))→(letrecEnv1,Env2 ins)

Wenn der mittlere letrec-Ausdruck einen ∀-Typ hat, dann werden die darin

(23)

gebundenen Typvariablen unter einen Allquantor im Typ des ¨außerenletrec- Ausdrucks verschoben. Die Typen der letrec-Ausdr¨ucke bleiben dabei gleich oder werden verallgemeinert und die Typeinschr¨ankungen sind somit f¨ur den resultierenden letrec-Ausdruck weiterhin erf¨ullt.

3.6.2 (llet-e)

(letrec Env1, x = (letrec Env2 in s :: ∀Y.T0) :: ∀X.T in t) → (letrecEnv1, x=s::∀Y.T0,Env2 int)

Zuerst muss die Verallgemeinerung, die durch Satz 5 beschrieben wird auf den letrec-Ausdruck angewendet werden, der an xgebunden ist, so dass der All- quantor auf die Typen in der Umgebung des innerenletrec-Ausdrucks verteilt wird und dadurch∀X.T und∀Y.T0 der gleiche Typ sind und keine Typvariable ausX in Env2 auftaucht. Dann kann der Reduktionsschritt durchgef¨uhrt wer- den.

Der Typ vonxund des daran gebundenen Ausdrucks wird beibehalten und die Typeinschr¨ankungen werden also durch die Reduktion nicht verletzt.

3.6.3 (lapp)

((letrecEnv ins::∀X.S) :: (∀X.S) t::T)

→ (letrecEnv in((s::∀X.S)t::T))

wobei wieder die Unabh¨angigkeitseigenschaften ausgenutzt werden, um die∀- gebundenen Typvariablen zu verteilen.

Die anderen Regeln entsprechen (lapp)

3.7 Rule (lbeta)

Die Regel (lbeta) verlangt nach einer Vorverarbeitung der Typen. Dabei wird der Typ der Abstraktion an den verwendeten Typ der rechten Seite der Anwendung angepasst. Im Gegensatz zu den Regeln (llet-e) und (lapp) wird der Typ hier also spezialisiert.

3.7.1 Typ-Instanziierung vor (lbeta)

F¨ur gegebene Ausdr¨ucke λx.s :: ∀X.S, t :: ∀Y.T, mit S =Sx →Ss, so dass die Anwendung den Typ ((λx.s)t) ::∀Z.R hat, muss zuerst der TypSx vonx instanziiert werden, um ihn an den verwendeten Typ anzupassen.

Sei U := FVtype(Sx)∩ X. Wenn U = ∅, dann ist nichts zu tun. Sonst, sei ρ die Substitution, die nach den Einschr¨ankungen f¨ur Anwendungstypen exis- tiert, mit Dom(ρ) = X ∪ Y,Z = VCod(ρ) \ FVtype(∀X.S,∀Y.T), so dass ρ(S) =S1 → R, ρ(T) =S1. Nun seiρ0 :=ρ|U0 wird f¨ur die Instanziierung des Typs von x und des K¨orpers der Abstraktion d.h. s genutzt. Die Instan- ziierung ist mit einer Umbenennung von x nach x0 :: ρ0(S1) in s verbunden.

Ebenso muss f¨ur alle anderen Variablen verfahren werden, deren Typ einen Typ ausU enth¨alt - was lediglich f¨ur Variablen der Fall sein kann, die insgebunden sind. Diese kombinierte Instanziierung ist m¨oglich, da der Typ vonxrespektive x0 monomorph innerhalb des K¨orpers der Abstraktion ist. Der Ausdruck bleibt wohl-getypt. Der Typ der Anwendung ((λx.s) t) ist durch die Instanziierung

(24)

nicht betroffen.

Es gibt drei F¨alle, die eine Instanziierung n¨otig machen:

1. Die Anwendung ist im Eingabe-Ausdruck

2. Die Anwendung wurde durch ein (cp) erzeugt, so dass die Abstraktion an die Position der Funktion kopiert wurde

3. Die Abstraktion wurde durch ein (lapp) erzeugt 3.7.2 Anwendung von (lbeta)

(lbeta) wird erst angewendet, nach dem die Instanziierung durchgef¨uhrt wurde.

Sei die Applikation (λx.s::∀X.S t::∀Y.T).ρ0, das weiter oben definiert wurde, ist eine injektive Substitution von Typvariablen zu Typvariablen. Das Ergebnis ist (letrecx=tins), wobei die Typanpassung wie folgt aussieht:xist getypt wie in der Abstraktion, die Typvariablen inU :=FVtype(Sx)∩ X sind im ¨außeren letrec gebunden. Mit den Unabh¨angigkeitseigenschaften ist es sp¨ater wieder m¨oglich die Quantoren zu verteilen undxwieder polymorph zu machen.

3.8 Rule (cp)

(letrecx::S1=t::S2 inC[x::S1])

→ (letrecx=tinC[t0::S2]).

Hier ist t0 eine α-umbenannte Kopie von t wobei die Typen ebenfalls kopiert undα-umbenannt werden, falls notwendig.

3.9 Rule (case)

(caseK (cK,i s1. . . sn) of(cK,ix1. . . xn)→ti, . . . alts)

→ (λx1. . . xn.ti)s1. . . sn)→(letrecx1=s1, . . . xn=sn inti).

Der Zwischenschritt vereinfacht es, die Behandlung der Typen zu erkl¨aren, die wie folgt aussieht: Wenn (cK,i s1. . . sn) einen∀-quantifizierten Typ hat, dann ist es erlaubt die Typ-Variablen aller si unabh¨angig zu machen, sofern diese in (cK,i s1. . . sn) gebunden werden.

Die Typen der Unterausdr¨uckesi haben ihre eigenen Allquantoren f¨ur die un- abh¨angigen Typvariablen. Die Behandlung der Typen und die weiteren Schritte entsprechen einemn-fachen (lbeta) mit darauffolgenden (llet-in) Reduktionen.

3.10 Rule (abs)

Die Reduktion ist (letrec x = (c t1. . . tn),Env in r) → (letrec x1 = t1, . . . , xn = tn, x = (c x1. . . xn),Env in r). Hier wird davon ausgegangen, dass die Typen der xi die gleichen sind, wie die Typen der entsprechenden ti, i = 1, . . . , n, und die Unabh¨angigkeitseigenschaft auf den Let-Ausdruck ange- wandt wurde, wobei eine (llet-e) Reduktion eingeschoben wurde.

Dann haben (c t1. . . tn) und (c x1. . . xn) korrespondierende Typen und die Bindungsstruktur wird nicht verletzt.

(25)

4 Implementierung

Die Aufgabenstellung der Bachelorarbeit sah vor, dass zuerst eine Datenstruk- tur zur Darstellung der mit Typen markierten Ausdr¨ucke des parametrisch- polymorphen λ-Kalk¨uls zu implementieren sei. F¨ur diese Datenstruktur sollte daraufhin eine ¨Uberpr¨ufung der Typkonsistenzregeln und Reduktion einschließ- lich der Typ¨anderungen in ein Programm ¨uberf¨uhrt werden. Die Implementie- rung sollte mit einer hinreichenden Zahl an Testausdr¨ucken ¨uberpr¨uft werden.

Optional sollte ein Parser f¨ur die Ausdr¨ucke entwickelt werden.

Als Implementierungssprache wurde Haskell vorgegeben. Alle Aufgaben wurden von mir bearbeitet. Die nichtdeterministischen Bestandteile des Kalk¨uls wurden nach Absprache bei der Implementierung des Programms nicht ber¨ucksichtigt, da sie f¨ur die experimentelle Untersuchung des Typsystems nicht wesentlich sind.

Im folgenden Abschnitt sollen die Struktur und ausgew¨ahlte Teile des Pro- gramms vorgestellt werden. Insbesondere werde ich versuchen, dabei zu doku- mentieren, an welchen Stellen Probleme bei der Implementierung aufgetreten sind.

4.1 Aufbau der Implementierung

Wie alle modernen Programmiersprachen verf¨ugt Haskell ¨uber ein hierarchisches Modulsystem. Die Implementierung macht von diesem zur Strukturierung des Programms Gebrauch. Neben Modulen, die Tests des Programmes enthalten und die im Namensraum T est.∗ untergebracht sind, besteht die wesentliche Funktionalit¨at des Programms aus den Modulen im NamensraumInterpreter.

4.2 Definitions

Aus dem Modul Definitions sollen hier haupts¨achlich die Datentypen f¨ur Ausdr¨ucke und Typmarkierungen vorgestellt werden. Abgesehen davon, dass dessen Entwurf nat¨urlich wesentliche Voraussetzung f¨ur die Erf¨ullung der weiterf¨uhrenden Aufgaben war, handelte es sich dabei auch selbst um einen expliziten Bestandteil der Aufgabenstellung.

Dabei konnte die Syntax der Ausdr¨ucke (siehe Abbildung 4 auf Seite 15) auf eine sehr direkte Weise auf drei Haskell Datentypen - Expr, CaseAlternative f¨ur Case-Alternativen und Binding f¨ur Bindungen von Variablen in Let- Ausdr¨ucken - ¨ubertragen werden. Diese sind in Abbildung 8 auf der n¨achsten Seite dargestellt. Da die Formulierung des Typsystems ¨uber Konsistenzregeln als Grundlage f¨ur die Implementierung diente, ist jeder Ausdruck zus¨atzlich mit einer Typmarkierung versehen. F¨ur die Darstellung der Typmarkierungen dient der Datentyp T ypeM ark aus Abbildung 9 auf der n¨achsten Seite. Auch dieser konnte unmittelbar aus der formalen Beschreibung in [SSS]

”uber-¨ setzt“ werden. T V x ist dabei eine Typvariable x, T F n t t0 ist der spezielle Typkonstruktor f¨ur Funktionstypen t → t0, T C c ts ein beliebiger anderer Typkonstruktor (c t1· · ·tn), wobei diet1,· · ·, tn die Elemente der Listetssind undU niversalQ tvs tmein Allquantor ist, der alle Typvariablen austvsintm bindet. Die Menge der Typkonstruktoren und zugeh¨origen Datenkonstruktoren wird im Code festgelegt. F¨ur jeden Typkonstruktor enth¨alt die Liste typeCon- structors ein Tupel, das dem Typ seine Typvariablen zuordnet (f¨ur Listen ist

(26)

d a t a E x p r = V V a r i a b l e

| App E x p r E x p r T y p e M a r k

| Abs V a r i a b l e E x p r T y p e M a r k

| Seq E x p r E x p r T y p e M a r k

| L e t r e c [ B i n d ] E x p r T y p e M a r k

| C C o n s t r u c t o r N a m e [ E x p r ] T y p e M a r k

| C a s e C o n s t r u c t o r N a m e E x p r [ C a s e A l t e r n a t i v e ] T y p e M a r k d e r i v i n g ( Show , Eq )

d a t a C a s e A l t e r n a t i v e = Alt C o n s t r u c t o r N a m e [ V a r i a b l e ] E x p r T y p e M a r k t y p e C o n s t r u c t o r N a m e = S t r i n g

d a t a B i n d = V a r i a b l e :=: E x p r d e r i v i n g ( Show , Eq )

Abbildung 8: Datentypen f¨ur Ausdr¨ucke des Kalk¨uls

d a t a T y p e M a r k

= TV T y p e V a r i a b l e

| TFn T y p e M a r k T y p e M a r k

| TC S t r i n g [ T y p e M a r k ]

| U n i v e r s a l Q [ T y p e V a r i a b l e ] T y p e M a r k d e r i v i n g ( Show , Eq )

t y p e T y p e V a r i a b l e = S t r i n g

Abbildung 9: Haskell Typ f¨ur Typmarkierungen

dies z.B. List a).

t y p e C o n s t r u c t o r s = [( " B o o l " , []) , ( " L i s t " , [ " a " ]) , ( " Nat " , [ ] ) ]

Eine entsprechende Liste ordnet einem Typ seine Datenkonstruktoren mit den jeweiligen Typen ihrer Argumente zu, wobei vorausgesetzt wird, dass die Typ- variablen in den Datenkonstruktoren genau die gleichen Bezeichnungen haben, wie in den Typkonstruktoren.

d a t a C o n s t r u c t o r s = [( " B o o l " , [( " T r u e " , []) , ( " F a l s e " , [])]) ,

( " L i s t " , [( " C o n s " , [( TV " a " ) , ( TC " L i s t " [ ( TV " a " )])]) , ( " Nil " , [])]) ,

( " Nat " , [( " S " , [ TC " Nat " []]) , ( " Z " , [ ] ) ] ) ]

Sowohl der Typcheck, als auch die Reduktion, sind innerhalb einer beliebigen State Monade implementiert. Beide Funktionen haben also einen Zustand, der in diesem Modul in Form des Environment Typs definiert ist. Dieser dient unter anderem dazu, w¨ahrend der jeweiligen Berechnung ¨uber die verwendeten Varia- blen in einem Ausdruck (bzw. Typvariablen in einem Typ) Buch zu f¨uhren und erm¨oglichen somit die Generierung frischer Variablennamen, wie sie an mehreren Stellen ben¨otigt wird.

(27)

4.3 Substitution

Neben dem Datentyp f¨ur Substitutionen und den zugeh¨origen Funktionen zur Konstruktion und Anwendung dieser, enth¨alt dieses Modul vor allem auch die Implementierung des Unifikationsalgorithmus. Diese ist angelehnt an die Imple- mentierung aus [Pie02] und wurde nur um eine Dekomposition von Datenkon- struktoren erweitert. Die Unifikation erh¨alt einen “Constraint Stack” und be- rechnet einen allgemeinsten Unifikator, der die dadurch definierten Constraints einh¨alt.

u n i f y [] = r e t u r n Set . e m p t y u n i f y ( c@ ( t : - > t ’): cs )

| t == t ’ = u n i f y cs

| i s T V t && n o t O c c u r s t t ’ = a d d S u b ( t v V a r t ) t ’ cs

| i s T V t ’ && n o t O c c u r s t ’ t = a d d S u b ( t v V a r t ’) t cs

| i s T F n t && i s T F n t ’ = let ( TFn a b ) = t ( TFn a ’ b ’) = t ’

in u n i f y (( a : - > a ’ ) : ( b : - > b ’): cs )

| i s T C t && i s T C t ’ && t c N a m e t == t c N a m e t ’ =

let cs ’ = ( z i p W i t h (: - >) ( t c T y p e s t ) ( t c T y p e s t ’ ) ) + + cs in u n i f y cs ’

| o t h e r w i s e = a b o r t " F a i l e d to u n i f y c o n s t r a i n t s "

w h e r e a d d S u b tv t cs =

do let ds = ( tv : - > t ) c = ( TV tv ) : - > t

s < - u n i f y ( a p p l y S u b T o C s ds cs ) r e t u r n $ Set . i n s e r t ds s a p p l y S u b T o C s s cs =

[ ( a p p l y D s s t : - > a p p l y D s s t ’) | ( t : - > t ’) < - cs ] n o t O c c u r s tv t = n o t M e m b e r ( t v V a r tv ) ( f r e e t v s t )

Die Unifikation selbst wurde also auf die ¨ubliche Weise in ein Programm umge- setzt. Die Anforderungen an die Substitutionen, die in den Typkonsistenzregeln verlangt werden, erlauben es jedoch nicht, diese in direkter Weise mittels Uni- fikation zu bestimmen.

4.3.1 Substitutionen mit eingeschr¨anktem Bereich

Jede der Typkonsistenzregeln fordert die Existenz einer Typsubstitution mit einem bestimmten Bereich A, die Einschr¨ankungen bez¨uglich der Bilder be- stimmter Typvariablen gen¨ugen muss.

Die Unifikation findet eine Substitution, die die Einschr¨ankungen erf¨ullt, sofern diese existiert. Welche Typvariablen dabei aber konkret abgebildet werden, ist davon abh¨angig, wie die Einschr¨ankungen in den Unifikationsalgorithmus ein- gegeben werden. Daher reicht die ¨Uberpr¨ufung des Bereichs der berechneten Substitution nicht, da dies dazu f¨uhren k¨onnte, dass zul¨assige Typmarkierun- gen abgelehnt werden, weil die Unifikation eine falsche Substitution bestimmt.

Statt dessen muss erzwungen werden, dass die Unifikation nur Substitutionen mit einem bestimmten Bereich berechnet. Das Problem wird sp¨ater in Beispiel

Abbildung

Abbildung 1: Haskell Beispiel, Modul Lang.Expressions
Abbildung 6: Redex-Suche mit Markierungen
Abbildung 7: Normalordnungsregeln

Referenzen