3.4 Metaprogrammierung

Was ist Metaprogrammierung?

Für Metaprogrammierung gibt es verschiedene Definitionen und Erklärungen; die Folgenden stammen aus dem Buch [MetaprogDotNet].

  1. A computer program that writes new computer programs. [MetaprogDotNet, S. 6]
    • Code-Generierung ist in der Tat ein wichtiges Gebiet der Metaprogrammierung. Immerhin müssen Compiler den Quellcode der Programmiersprache in Maschinen-nahen Code umsetzen. Dennoch schreiben nur wenige Programmierer jemals selbst einen Compiler. Nur selten wird Code generiert, z.B. im Fall von Datenbank-Programmierung und der Generierung von Klassen aus dem Datenbank-Schema. Eine anderer anwendungsbezogener Aspekt von Metaprogrammierung ist Analyse oder Inspektion von Programmen (bzw. von Objekten) zur Laufzeit (dies ist das Gebiet Reflection von Programmiersprachen).
  2. Try to think of it [metaprogramming] as after-programming or beside-programming. The Greek prefix meta allows for both of those definitions to be correct. Most of the examples in this book demonstrate programming after traditional compilation has occurred, or by using dynamic code that runs alongside other processes. [MetaprogDotNet, S. 6]
    • Die Umschreibung mit Danach-Programmierung ist bei Codetransformation sehr passend, weil vom Benutzer geschriebener Code vor dem eigentlichen Kompilieren um Aspekte wie Persistenz von ObjektenWie etwa für das Serializable-Attribut bei der Programmiersprache Nemerle: http://nemerle.org/wiki/index.php?title=Macros_tutorial#Macros_in_custom_attributes. oder DatenprüfungenWie in Design-by-Contract-Frameworks für NotNull-Attribute. erweitert wird. Nebenbei-Programmierung kann das Arbeiten mit generiertem Code sein, d.h. beim Programmieren kann auf generierte Klassen und Methoden zugegriffen werden. Das ist insbesondere wichtig bei GUI-Programmierung, wenn GUI-Designer Code erzeugen, den der Programmierer sofort verwenden muss, z.B. Referenzen auf Steuerelemente; oder auch bei aus Datenbank-Schemata generierten Klassenbibliotheken.

Im Folgenden wird Metaprogrammierung anhand von Syntaxbäumen erklärt, weil diese auf anschauliche Weise zeigen, dass Programmcode Daten sind und die übliche Datenverarbeitung dadurch zur Verarbeitung von Programmen wird.

Quotations



Es kann vorteilhaft sein, auf Teile des Quellcodes als Objekt zugreifen zu können, z.B. um diesen zu analysieren, zu transformieren, zu persistieren oder zu übertragen. In F# sind Quotations Ausdrücke der Form <@ Ausdruck @>. Innerhalb dieser Klammern lässt sich Quellcode schreiben, der nicht ausgewertet oder kompiliert wird, sondern dessen Syntaxbaum erzeugt wird. Die Einschränkung ist, dass keine Typen und Module deklariert werden können. Wenn Funktionen geschrieben werden, lässt sich über des ReflectedDefinition-Attributes ausdrücken, dass auch auf die quotierte Form der Funktion zugegriffen werden kann, wie folgendes Beispiel zeigt:

> [<ReflectedDefinition>]
  let f(x: int) = 2 * x;;
val f : int -> int

> open Microsoft.FSharp.Quotations
  open Microsoft.FSharp.Quotations.Patterns
  open Microsoft.FSharp.Quotations.DerivedPatterns;;
> match <@ f @> with
  | Lambda(param,
           Call(target, MethodWithReflectedDefinition def, args)) -> def;;
warning FS0025: Incomplete pattern matches on this expression.
val it : Expr = Lambda (x,
  Call (None, Int32 op_Multiply[Int32,Int32,Int32](Int32, Int32),
        [Value (2), x]))
{CustomAttributes = ...;
 Type = Microsoft.FSharp.Core.FSharpFunc`2[System.Int32,System.Int32];}

Mithilfe von Quotations lassen sich F#-Ausdrücke möglich, die keinem typischen F#-Programm ähneln. Z.B. lässt sich das Member-Prolog-Prädikat bei geeigneter Definition von memb, E, __ und R in F# schreibenIm Abschnitt Quotations des Anhangs finden sich besagte Definitionen.. Dieses Programm ließe sich in den entsprechenden Prolog-Code transformieren. Dies ist ein Schritt in polyglotte (mehrsprachige) Programmierung, bei der man verschiedene kompatible Programmiersprachen zur Lösung eines Problems verwendet.

prolog <@
        memb(E, E :: __)
        memb(E, __ :: R) <-- memb(E, R)
@>

Für Codegenerierung sind Syntax-Bäume ein gutes Hilfsmittel. Andernfalls müsste man auf Generierung von Quelltext in Form von Strings zurückgreifen, wobei dies schnell zur Generierung von fehlerhaftem Code führen kann. Andere Aufgaben wie Code-Optimierung und -Transformation können nicht sinnvoll auf der Basis von Strings durchgeführt werden, weil Fallunterscheidungen anhand von Strings fehlerträchtig und aufwendig sind. Syntaxbäume werden z.B. bei der Überführung von Ausdrücken in der Programmiersprache in SQL-Befehle verwendet.

Bearbeitet: Code-Strings sind auf der falschen Abstraktionsstufe.

Metaprogrammierung und DSLs stehen in starkem Zusammenhang, in dem oben genannten Buch werden DSLs durch Metaprogrammierungstechniken ermöglicht [MetaprogDotNet, vgl. S. 5]

Quotations und Computation-Expressions werden oft für Metaprogrammierungszwecke eingesetzt. Ein beliebtes Beispiel sind Datenbank-Queries, die in SQL umgesetzt werden können.

query{ for customer in db.Customers do
         where customer.ID = 42 
         select customer.Name
}

Dieser Ausdruck kann innerhalb der Entwicklungsumgebung geschrieben werden, wobei nur gültige Abfragen formuliert werden können. Der Ausdruck kann in den SQL-Befehl select Name from Customers where ID = 42 übersetzt werden.

Mit Metaprogrammierung ist es möglich, von Computer-nahen Möglichkeiten zu abstrahieren. Das gilt insbesondere für konkrete Techniken der Fallunterscheidungen, die in den Abschnitten Pattern matching und Imperativ-objektorientierte Fallunterscheidungen betrachtet wurden. Damit wird die Aufgabe der Umsetzung auf einen späteren Zeitpunkt verschoben.

Somit kann die Ausführungsumgebung eine andere sein als die standardmäßig verwendete; dies wurde bereits mit einem F#-zu-JavaScript-Übersetzerhttp://fsharp.org/use/html5/ und Ausführung einer Untermenge von F#-Konstrukten auf Grafikkarten durchgeführt http://fsharp.org/use/gpu/. Details zu der GPU- und SQL-Übersetzung finden sich in [FSharpMeta, S. 48, 50].