2 Grundlagen
zum Anfang der Seite
In diesem Kapitel werden die für das weitere Verständnis dieser Arbeit benötigten Grundlagen erläutert.
2.1 Klassifikation von Metasprachen
zum Anfang der Seite

Metasprachen lassen sich anhand von drei Merkmalen klassifizieren. Das erste Merkmal beschreibt den Bearbeitungszeitpunkt des Objektprogramms. Je nachdem, ob das Objektprogamm also zur Laufzeit oder zur Compilezeit generiert wird, unterscheidet man zwischen dynamischen und statischen Metasprachen.6 Als Beispiel für eine dynamische Metasprachen kann die Programmiersprache Java unter Verwendung der Reflection Api gesehen werden. Im Gegenteil dazu ist Template Haskell rein Compilezeit-orientiert - also statisch.

Ein zweites Klassifizierungsmerkmal beschreibt die Heterogenität von Objekt- und Metasprache. Heterogene Metasprachen sind solche, welche nicht die gleiche Sprache verwenden wie das Objektprogramm.7 Als Beispiel dafür kann die bereits weiter oben erwähnte Programmiersprache PHP zur Erzeugung von Java Script Code gesehen werden. Template Haskell ist dagegen eine homogene Metasprachen, da sie Haskell verwendet um Haskell zu erzeugen.

Das dritte und letzte Merkmal zur Klassifizierung von Metasprachen ist die Stufigkeit. Mehrstufige Metasprachen erzeugen Objektprogramme, die ihrerseits wiederum in einer Metasprache verfasst sind.8 Template Haskell ist eine einstufige Metasprache, da aus einem Template Haskell Metaprogramm ausführbarer Haskell Code erzeugt wird.

2.2 Statisches und dynamisches Scoping
zum Anfang der Seite

Unter einem Scope kann ein in sich abgeschlossener Bereich oder Kontext betrachtet werden, innerhalb dessen bestimmte Bindings gelten. Ein Binding ist die Zuweisung eines Ausdrucks (oder allgemein: Objekt) an einen Namen. Scoping Regeln beschreiben, wie und von wo aus im Programm die Bindings eines Scopes zugreifbar sind. Prinzipiell lässt sich zwischen dem statischen (lexikalischen) Scoping und dem dynamischen Scoping unterscheiden, auf deren Unterschiede im Folgenden eingegangen wird.9

Statisches Scoping

Bei Sprachen, die statisches Scoping verwenden, werden die Bindings zwischen Namen und Objekten zur Compile-Zeit bestimmt. Der Kontrollfluss des Programms zur Ausführungszeit wird somit nicht berücksichtigt. Das "aktuell" gültige Binding für einen Namen findet sich in jener Matching-Deklaration, welche den aktuellen "Punkt" im Programm am nächsten umschließt, man könnte auch sagen: der innerste Block bildet den Scope. Haskell verwendet statisches Scoping, wie in dem folgenden Beispiel ersichtlich wird10:

  1. x = 0
  2. bar = x
  3. foo = let x = 1 in bar  -- x wird lokal definiert 

Die Redefinition von x in der Funktion foo wird vom let-Block am nächsten umschlossen und gilt daher lokal innerhalb diesem. In foo wird der Ausdruck bar ausgewertet, der ein Top-Level-Binding aufweist, welchem wiederum das Top-Level-Binding von x zugewiesen ist. Da die Top-Level-Definition von x in einem äußeren Scope ist, wird diese nicht vom let-Block beeinflusst. Ein Aufruf der Funktion foo liefert somit das Ergebnis: 0.

Dynamisches Scoping

Die Vorgehensweise beim dynamischen Scoping lässt sich anschaulich mithilfe eines Stacks beschreiben. Jeder Name hat einen Stack von Bindings, wobei stets das oberste Element eines solchen Stacks das aktuelle Binding für den zugehörigen Namen darstellt. Beim Finden eines neuen Bindings, wird dieses auf den Stack gelegt. Beim Verlassen eines Kontrollflusses (bspw. einer Funktion) wird das oberste Element vom Stack entfernt. Bezogen auf das obige Beispiel, führt dies dazu, dass der Binding-Stack für x beim Betreten der Funktion foo zunächst nur die 0 als oberstes Element enthält. Die Definition von x in foo legt die 1 auf den Stack. Der Aufruf von bar in foo liefert dann für x das nun oberste Element 1, da der Kontrollfluss von foo noch nicht beendet ist. 11

2.3 GHC-Phasen und Template Haskell
zum Anfang der Seite
Der Gnu Haskell Compiler durchläuft stets zwei aufeinanderfolgende Phasen. In der ersten Phase, werden die Scopes für alle in der Quelle vorkommenden Namen aufgelöst und die korrekten Bindings bestimmt, sodass keine Namenskonflikte entstehen können - es handelt sich hier also um statisches Scoping.12
  1. let x = \x -> x+1 in x
In dem obigen Beispiel soll der Ausdruck x anhand einer Lambda-Funktion berechnet werden, deren einziger Parameter wiederum x heißt. Das Ergebnis wird dann dem Ausdruck x zugewiesen. Es ist ganz offensichtlich, dass das x im Let-Ausdruck und das x im Lambda-Ausdruck nicht identisch sein können, da die Berechnung andernfalls zu einer Endlosrekursion führen würde. Die x-Vorkommen gehören demnach zu unterschiedlichen Scopes, deren Festlegung der GHC mittels Umbenennung (Nummerierung) der einzelnen Variablen erreicht. Das Ergebnis nach der Umbenennung des obigen Codes sieht dann bspw. folgendermaßen aus:13
  1. let x_77 = \x_78 -> x_78 + 1 in x_77
Die zweite Phase bildet die Typüberprüfung, welche den zuvor umbenannten Code hinsichtlich der Typrestriktionen prüft.

Valid XHTML 1.0 Strict