<!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD v1.0 20120330//EN" "JATS-archivearticle1.dtd">
<article xmlns:xlink="http://www.w3.org/1999/xlink">
  <front>
    <journal-meta />
    <article-meta>
      <title-group>
        <article-title>x iP L</article-title>
      </title-group>
      <contrib-group>
        <contrib contrib-type="author">
          <string-name>TS OlM</string-name>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <contrib contrib-type="author">
          <string-name>Christian Heinlein</string-name>
          <email>christian.heinlein@htw-aalen.de</email>
          <xref ref-type="aff" rid="aff0">0</xref>
        </contrib>
        <aff id="aff0">
          <label>0</label>
          <institution>Hochschule Aalen − Technik und Wirtschaft</institution>
        </aff>
      </contrib-group>
      <fpage>193</fpage>
      <lpage>212</lpage>
      <abstract>
        <p>MOSTflexiPL (Modular, Statically Typed, Flexibly Extensible Programming Language) ist eine statisch typisierte Programmiersprache, die vom Anwender nahezu beliebig erweitert und angepasst werden kann. Viele Syntaxerweiterungen, beispielsweise neue Operatorsymbole und Kontrollstrukturen, die in MOSTflexiPL unter einem sehr allgemeinen Operatorbegriff subsumiert werden, lassen sich genauso leicht definieren wie Funktionen in anderen Sprachen. Manche Erweiterungen, insbesondere die Einführung neuer Deklarationsformen, erfordern jedoch die in diesem Artikel vorgestellten virtuellen Operatoren, die einfache Syntaxtransformationen zur Übersetzungszeit ermöglichen.</p>
      </abstract>
    </article-meta>
  </front>
  <body>
    <sec id="sec-1">
      <title>1 Einleitung</title>
      <p>Copyright © 2014 for the individual papers by the papers’ authors. Copying permitted for private and
academic purposes. This volume is publisehd and copyrighted by its editors.
tiver Syntax, bei der die Klammern und das Komma einfach weitere Namen des
Operators sind) oder print• (mit moderner funktionaler Syntax).</p>
      <p>Tatsächlich erfolgt die Definition von Operatoren sehr ähnlich wie die Definition
von Funktionen in anderen Sprachen. Und da mit jedem neuen Operator „nebenbei“
auch ein neues syntaktisches Konstrukt definiert wird, werden Syntaxerweiterungen
auf die gleiche Art und Weise erstellt wie gewöhnliche Programme, d. h. es gibt
hierfür keinen separaten Spezialmechanismus mit eigenen Ausdrucksmitteln und Regeln.
Da jedes syntaktische Konstrukt durch einen Operator repräsentiert wird, stellt jede
Verwendung eines Konstrukts eine Operatoranwendung, d. h. einen Ausdruck dar.
Hierbei werden auch Konstanten und Variablen sowie Literale wie z. B. 0 oder "abc"
als nullstellige Operatoren aufgefasst, deren Anwendung einfach den jeweiligen Wert
liefert. Beispielsweise ist der Ausdruck if x &gt;= 0 then x else −x end eine
Anwendung des Operators if•then•else•end auf die Operanden x &gt;= 0 sowie x und −x.
Der Teilausdruck x &gt;= 0 ist wiederum eine Anwendung des Operators •&gt;=• auf die
Operanden x und 0, bei denen es sich um Anwendungen der nullstelligen
Operatoren x (eine Konstante oder Variable) und 0 (ein Literal) handelt. Ebenso ist −x eine
Anwendung des Operators −• auf den Operanden x.</p>
      <p>Die Anwendungsmöglichkeiten von MOSTflexiPL sind vielfältig. In erster Linie ist
es als Allzwecksprache (general purpose language) gedacht, mit der man je nach
persönlicher Präferenz sowohl imperativ als auch funktional programmieren kann. Der
entscheidende Unterschied und Vorteil gegenüber anderen Sprachen besteht darin,
dass man bei Bedarf jederzeit syntaktische Erweiterungen vornehmen kann, um
bestimmte Dinge einfacher, kürzer, „natürlicher“ oder verständlicher ausdrücken zu
können. Derartige Erweiterungen können entweder ad hoc für ein einzelnes Programm
definiert werden oder aber in wiederverwendbaren Operatorbibliotheken
zusammengefasst werden, die auch anderen Benutzern zur Verfügung gestellt werden können.
Da sich Spracherweiterungen sehr leicht definieren und auch wieder ändern lassen,
kann MOSTflexiPL auch als Experimentierplattform für neue Spracherweiterungen
(z. B. für nebenläufige oder natürlichsprachliche Programmierung) verwendet werden.
Weil sich die Syntax der Sprache nicht nur erweitern, sondern auch beliebig
verändern und einschränken lässt, kann MOSTflexiPL schließlich auch als Hilfsmittel zur
Definition und Implementierung anwendungsspezifischer Sprachen (DSLs) verwendet
werden.</p>
      <p>Die vordefinierten Grundkonstrukte und -konzepte der Sprache (die zum Teil aus
dem Vorgängerprojekt APPLEs [He05, He07] stammen), insbesondere auch das
vorhandene Typsystem mit beschränkter parametrischer Polymorphie sowie
benutzerdefinierbaren impliziten Typumwandlungen, sind so allgemein und ausdrucksstark, dass
nicht nur „syntaktischer Zucker“, sondern auch weitreichende „paradigmatische“
Erweiterungen definiert werden können, beispielsweise objektorientierte Klassen (mit
einfacher, mehrfacher und wiederholter Vererbung) und Methoden (mit einfachem,
mehrfachem und prädikatbasiertem dynamischen Binden) oder aspektorientierte
Konstrukte wie Inter-Typ-Deklarationen und Advice. Auch deklarative Konstrukte, wie sie
z. B. bei logischer Programmierung verwendet werden, lassen sich prinzipiell als
Spracherweiterungen definieren. Die Tatsache, dass sich Benutzer der Sprache bei
Bedarf nahezu alles Gewünschte selbst definieren (oder aus Bibliotheken importieren)
können, ist auch der Grund, warum bestimmte, aus anderen Sprachen gewohnte
„Bequemlichkeiten“ a priori nicht vorhanden sind.</p>
      <p>Im nachfolgenden Abschnitt 2 werden einige typische Beispiele für
Syntaxerweiterungen durch neue Operatoren vorgestellt und damit nebenbei wichtige
Grundkonstrukte von MOSTflexiPL vorgestellt und erläutert. Abschnitt 3 zeigt die Grenzen
dieses Ansatzes auf und erläutert, wiederum anhand von Beispielen, wie sie mit Hilfe
virtueller Operatoren überwunden werden können. Abschnitt 4 demonstriert die
Anwendungsmöglichkeiten dieses neuen Konzepts anhand eines komplexeren Beispiels.
Abschnitt 5 vergleicht das Konzept mit verwandten Ansätzen, während Abschnitt 6
mit Zusammenfassung und Ausblick schließt. Weitere Informationen zu
MOSTflexiPL sowie viele weitere Beispiele findet man auf http://flexipl.info.</p>
      <p>Ziel dieses Artikels ist nicht die Vorstellung von MOSTflexiPL an sich − hierfür sei
auf die o. g. Webseite sowie auf [He12] verwiesen, wo auch die wesentlichen
Implementierungsideen beschrieben werden − , sondern die Beschreibung virtueller
Operatoren. Obwohl diese in [He12] bereits erwähnt werden, wurden sie seitdem signifikant
weiterentwickelt, u. a. um die dort erwähnten Einschränkungen bezüglich
benutzerdefinierter Deklarationsoperatoren zu überwinden.</p>
    </sec>
    <sec id="sec-2">
      <title>2 Beispiele für Syntaxerweiterungen durch Operatoren</title>
      <sec id="sec-2-1">
        <title>2.1 Einfache Operatordeklarationen</title>
        <p>Die folgenden Zeilen zeigen eine einfache Operatordeklaration in MOSTflexiPL:
["n" : int]
n "2" : int
{ n * n }</p>
        <sec id="sec-2-1-1">
          <title>Parameterliste</title>
        </sec>
        <sec id="sec-2-1-2">
          <title>Signatur und Resultattyp</title>
        </sec>
        <sec id="sec-2-1-3">
          <title>Implementierung</title>
          <p>Sie besteht aus einer Parameterliste in eckigen Klammern, einer Signatur vor dem
Doppelpunkt, einem Resultattyp danach sowie einer Implementierung in
geschweiften Klammern. Die Signatur besteht ihrerseits aus Parametern und Zeichenketten in
Anführungszeichen und definiert die Syntax des Operators, d. h. die syntaktische
Form seiner Anwendungen: Jeder Parameter ist ein Platzhalter für einen Operanden,
d. h. für einen Teilausdruck mit entsprechendem Typ, während eine Folge beliebiger
Zeichen in Anführungszeichen einen Namen des Operators darstellt, der bei
Anwendungen des Operators genau so (allerdings ohne die Anführungszeichen)
hingeschrieben werden muss. Demnach ist z. B. (2+3)2 eine korrekte Anwendung des gerade
definierten Operators, weil (2+3) ein Teilausdruck mit Typ int und 2 der Name des
Operators ist.</p>
          <p>Die Parameterliste besteht aus Parameterdeklarationen (ggf. durch Strichpunkte
getrennt), bei denen es sich ebenfalls um einfache Operatordeklarationen handelt, die
(bei den hier betrachteten einfachen Beispielen) lediglich aus einer Signatur und
einem Resultattyp bestehen. Daher ist jedes Auftreten des Parameters n in Wirklichkeit
eine Anwendung des nullstelligen Operators mit Signatur "n" und Resultattyp int.</p>
          <p>Die Implementierung schließlich ist ein beliebiger Ausdruck, dessen Typ mit dem
Resultattyp übereinstimmen muss und durch dessen Auswertung das Ergebnis einer
Operatoranwendung entsteht. Beispielsweise entsteht der Wert des Ausdrucks
(2+3)2, indem zunächst der Parameter n mit dem Wert des zugehörigen Operanden
(2+3) (also 5) initialisiert wird und anschließend die Implementierung n * n
ausgewertet wird, die in diesem Fall den Wert 25 liefert.</p>
          <p>Ein weiterer Operator |•|, der den absoluten Betrag seines Operanden als Ergebnis
liefert, kann wie folgt definiert werden:
["x" : int]
"|" x "|" : int
{ if x &gt;= 0 then x else −x end }
Hier besteht die Implementierung aus einer Anwendung des vordefinierten
Verzweigungsoperators if•then•else•end, der, abhängig vom Wahrheitswert seines ersten
Operanden, entweder den Wert seines zweiten oder den seines dritten Operanden als
Ergebnis liefert. Da die Signatur aus einem senkrechten Strich in Anführungszeichen,
dem int-Parameter x und einem weiteren senkrechten Strich in Anführungszeichen
besteht, sind |0| und |2−5| exemplarische Anwendungen des Operators.</p>
          <p>Der nachfolgende Operator •! berechnet rekursiv die Fakultät von n:
["n" : int]
n "!" : int
{ if n &lt;= 1 then 1 else (n−1)! * n end }
Ähnlich wie bei rekursiven Funktionen in anderen Programmiersprachen, ist es
möglich, den gerade deklarierten Operator bereits in seiner eigenen Implementierung zu
verwenden.</p>
          <p>Wenn man die bis jetzt definierten Operatoren kombiniert, entsteht z. B. der
Ausdruck |2−5|!2 mit Wert 36. Um mehrere Ausdrücke nacheinander auszuwerten, kann
man sie mit dem vordefinierten Operator •;• verknüpfen, bei dessen Anwendung −
wie bei jeder Operatoranwendung − zunächst seine beiden Operanden (von links nach
rechts) ausgewertet werden und der als Ergebnis einfach den Wert seines rechten
Operanden liefert.</p>
        </sec>
      </sec>
      <sec id="sec-2-2">
        <title>2.2 Vorrang und Assoziativität von Operatoren</title>
        <p>Für int-Werte a, b und c besitzt der Ausdruck a + b2 zwei mögliche
Interpretationen: (a + b)2 und a + (b2). Um dem Compiler mitzuteilen, dass in diesem Fall
(wenn keine explizite Klammerung angegeben ist) die zweite Möglichkeit bevorzugt
werden soll (d. h. dass •2 stärker binden soll als •+•), kann die erste mit Hilfe einer</p>
        <sec id="sec-2-2-1">
          <title>Ausschlussdeklaration excl•end verboten werden:</title>
          <p>excl "a" : int; "b" : int; (a + b)2 end
Anschließend besitzt z. B. auch der Ausdruck a2 + b2 == c2 eine eindeutige
Interpretation, nämlich ((a2) + (b2)) == (c2), obwohl rein syntaktisch z. B. auch (((a2) +
(b2)) == c)2 möglich wäre. Da diese Möglichkeit jedoch nicht typkorrekt ist − der
Operator •==• besitzt Resultattyp bool, während der Operator •2 einen Operanden
mit Typ int verlangt − , wird sie vom Compiler automatisch ausgesondert, ebenso
wie alle weiteren denkbaren Möglichkeiten.</p>
          <p>Allgemein gesprochen, probiert der Compiler, wenn nötig, alle prinzipiell
möglichen Zerlegungen einer Eingabefolge in Ausdrücke und Teilausdrücke aus und
verwirft diejenigen, die nicht typkorrekt sind oder durch Ausschlussdeklarationen
verboten sind. Wenn dann genau eine Interpretationsmöglichkeit übrig bleibt, ist die
Eingabe korrekt, andernfalls ist sie entweder fehlerhaft oder mehrdeutig, was ebenfalls als
Fehler diagnostiziert wird.</p>
          <p>Dieser Ansatz erlaubt dem Benutzer, die Namen bzw. allgemeiner die Syntax von
Operatoren beliebig zu überladen. Beispielsweise gibt es neben dem Operator if•
then•else•end auch noch einen Operator if•then•end, sodass bei der
Verarbeitung der Eingabe if x &gt;= 0 then x else −x end erst beim Antreffen der
Zeichenfolge else klar wird, dass es sich um eine Anwendung des ersten Operators handelt.
Aber selbst wenn es neben einer int-Konstanten oder -Variablen mit dem Namen x
auch noch eine bool-Konstante mit dem gleichen Namen gäbe − ja, es ist möglich,
Operatoren so „stark“ zu überladen, dass sie sich nur noch in ihrem Resultattyp
unterscheiden − , wäre dieser Ausdruck immer noch eindeutig: Die Operatoren •&gt;=•
und −• akzeptieren nur Operanden mit numerischem Typ, daher kann bei den
Teilausdrücken x &gt;= 0 und −x jeweils nur das x mit Typ int gemeint sein. Das x zwischen
then und else könnte zunächst auch Typ bool besitzen, aber da zweiter und dritter
Operand von if•then•else•end den gleichen Typ besitzen müssen (der dann auch
den Resultattyp der Fallunterscheidung darstellt), bleibt auch hier schließlich nur die
Möglichkeit, das x mit Typ int zu verwenden.</p>
          <p>Natürlich ist es normalerweise ratsam, derartige „Überladungsrätsel“ zu vermeiden,
selbst wenn sie letztendlich eine eindeutige Lösung besitzen. Andererseits ist es
schwierig, allgemein zu definieren, welche Überladungen noch „sinnvoll“ sind und
welche nicht. Abgesehen davon, sind ähnliche Überladungen in natürlichen Sprachen
nichts Besonderes. Beispielsweise kann das Pronomen „sie“ eine oder mehrere
Personen bezeichnen, z. B. „sie geht“ oder „sie gehen“. Durch Berücksichtigung der
VerbEndung lässt sich der Konflikt leicht auflösen.</p>
        </sec>
      </sec>
      <sec id="sec-2-3">
        <title>2.3 Konstanten, Typen und Variablen</title>
        <p>Für einen beliebigen Typ T definiert eine Deklaration der Gestalt "x" : T eine
eindeutige Konstante des Typs T, d. h. einen nullstelligen Operator x, der bei jeder
Anwendung denselben eindeutigen Wert liefert und daher auch als statischer Operator
bezeichnet wird. (Operatoren mit Implementierung, wie z. B. "random" : int
{ ...... }, die prinzipiell bei jeder Anwendung einen anderen Wert liefern können,
werden zur Unterscheidung als dynamische Operatoren bezeichnet.)</p>
        <p>Wenn man für T den vordefinierten Metatyp type verwendet, z. B. "Account" :
type, erhält man einen neuen Typ Account, der verschieden von allen anderen
Typen ist. (Dementsprechend sind vordefinierte Typen wie z. B. int auch nichts anderes
als solche typwertigen Konstanten.) Anschließend kann man eindeutige Werte des
Typs wie z. B. "a" : Account definieren, die vergleichbar mit Objekten in anderen
Sprachen sind.</p>
        <p>Wenn eine Deklaration eines statischen Operators Parameter besitzt, z. B. ["T" :
type] "List" T : type, so liefert der dadurch definierte Operator List• für jeden
Wert seines Parameters T einen anderen eindeutigen Wert, sodass z. B. List int und
List Account verschiedene Werte des Typs type, d. h. verschiedene Typen sind.
Umgekehrt bezeichnet List int natürlich jedesmal den gleichen Typ.</p>
        <p>Allgemein liefert ein statischer Operator bei Anwendung auf die gleichen
Parameterwerte also immer das gleiche Ergebnis (was für einen dynamischen Operator mit
Implementierung wiederum nicht garantiert werden kann) und bei Anwendung auf
unterschiedliche Werte unterschiedliche Ergebnisse. Daraus folgt, dass zwei statische
Ausdrücke, d. h. Ausdrücke, die nur statische Operatoren enthalten, den gleichen Wert
liefern, wenn sie strukturgleich sind, d. h. wenn es sich um Anwendungen desselben
(statischen) Operators auf paarweise strukturgleiche Operanden handelt.</p>
        <p>Aufgrund dieser für den Compiler wichtigen Eigenschaft, dürfen statische
Operatoren als Typkonstruktoren verwendet werden, während dynamische Operatoren in
Typausdrücken verboten sind. Tatsächlich sind Typen in MOSTflexiPL einfach als
statische Ausdrücke mit Typ type definiert.</p>
        <p>Der vordefinierte Typkonstruktor •? liefert zu jedem Typ T den zugehörigen
Variablentyp T?, dessen Werte jeweils eindeutige Variablen mit Inhaltstyp T sind.
Beispielsweise deklariert "i" : int? eine Variable mit Inhaltstyp int, d. h. eine
eindeutige Speicherzelle zur Speicherung von int-Werten, deren Inhalt durch eine
Zuweisung wie z. B. i = i + 1 verändert werden kann.</p>
        <p>Eine parametrisierte Variablendeklaration wie z. B.
liefert für jeden Wert des Parameters a eine andere eindeutige Variable a.number,
d. h. sie ordnet jedem Konto eine Variable mit Inhaltstyp int zu, in der die Nummer
des Kontos gespeichert werden kann:
["a" : Account]
a "." "number" : int?
a.number = 4711;
print a.number
Auf diese Weise lassen sich indirekt Datenstrukturen definieren, die bei Bedarf
modular um neue „Attribute“ erweitert werden können (sog. offene Typen [He07]).</p>
      </sec>
      <sec id="sec-2-4">
        <title>2.4 Neue Kontrollstrukturen</title>
        <p>Die folgenden Zeilen zeigen eine einfache Anwendung des vordefinierten
Wiederholungsoperators while•do•end zur Ausgabe der ersten zehn Quadratzahlen:
"i" : int?;
i = 1;
while i &lt;= 10 do
print i2;
i = i + 1
end
"i" : int?;
for i = 1 .. 10 do</p>
        <p>print i2
end
Mit einer maßgeschneiderten for-Schleife, wie man sie aus vielen anderen Sprachen
kennt, könnte dies jedoch kürzer und übersichtlicher wie folgt formuliert werden:
Dieses Sprachkonstrukt kann sich ein Programmierer mit ein wenig Übung in
wenigen Minuten selbst definieren:
var = lower;
while var &lt;= upper do
body;
var = var + 1
end
Die einzige Besonderheit stellt der Parameter body für den Schleifenrumpf dar.
Erstens darf sein Typ beliebig sein, was durch den zusätzlichen deduzierten Parameter B
mit Typ type ausgedrückt wird. Zweitens soll der entsprechende Teilausdruck (im
obigen Beispiel print i2) in jeder Iteration − d. h. bei jeder Auswertung des
Parameters body in der Implementierung des Operators − erneut ausgewertet werden, d. h. er
darf nicht wie sonst als Wert übergeben werden, sondern als unausgewerteter
Ausdruck bzw. als anonyme parameterlose Funktion. Dies wird durch das geschweifte
Klammerpaar am Ende seiner Deklaration angezeigt.</p>
        <p>Da jeder Operator in MOSTflexiPL einen Resultattyp und -wert besitzen muss,
besitzt der Operator for•=•..•do•end, ebenso wie der vordefinierte Schleifenoperator
while•do•end, Resultattyp int und liefert als Resultatwert die Anzahl der
ausgeführten Iterationen.</p>
      </sec>
      <sec id="sec-2-5">
        <title>2.5 Sichtbarkeitsdeklarationen</title>
        <p>Für jeden Operator lässt sich mit Hilfe von Importdeklarationen festlegen, welche
anderen Operatoren in den einzelnen Operanden seiner Anwendungen sichtbar (und
damit verwendbar) sein sollen. Durch eine Exportdeklaration lässt sich außerdem
festlegen, ob in den Operanden deklarierte Operatoren über eine Anwendung des Operators
hinaus sichtbar sein sollen. Für den vordefinierten Operator •;• ist zum Beispiel
folgendes festgelegt:
• In seinem ersten Operanden ist alles sichtbar, was an der Anwendungsstelle des</p>
        <p>Operators sichtbar ist. (Der vordefinierte Standardfall für einen Operanden.)
• In seinem zweiten Operanden ist zusätzlich alles sichtbar, was im ersten Operanden
deklariert wurde.
• Über eine Anwendung des Operators hinaus ist alles sichtbar, was in einem seiner</p>
        <p>Operanden deklariert wurde.</p>
        <p>Durch das Zusammenspiel dieser Regeln entsteht der intuitiv erwartete Effekt, dass
ein Operator, der irgendwo in einer „Strichpunkt-Kette“ deklariert ist, an jeder „weiter
hinten liegenden“ Stelle der Kette sichtbar ist.</p>
        <p>Ändert man die letzte Regel dahingehend ab, dass über eine Anwendung des
Operators hinaus nur das sichtbar ist, was in seinem zweiten Operanden deklariert wurde,
so erhält man einen Operator, den man mit let•in•end bezeichnen kann und der
z. B. wie folgt zur Datenkapselung verwendet werden kann:
let</p>
        <p>"i" : int?; i = 0
in
"next" : int
{ i = i + 1 }
end;
print next;
print i</p>
      </sec>
    </sec>
    <sec id="sec-3">
      <title>3 Virtuelle Operatoren</title>
      <sec id="sec-3-1">
        <title>3.1 Motivation</title>
        <p>„Private“ Variable i mit Anfangswert 0
„Öffentlicher“ Operator next, der die hier
sichtbare Variable i inkrementiert und ihren Wert liefert</p>
        <sec id="sec-3-1-1">
          <title>Ausgabe: 1</title>
        </sec>
        <sec id="sec-3-1-2">
          <title>Fehler: i ist hier nicht sichtbar</title>
          <p>Wenn man var T als Synonym für Variablentypen T? (vgl. Abschnitt 2.3) verwenden
möchte, kann man versuchen, den Operator var• wie folgt zu definieren:
["T" : type]
"var" T : type
{ T? }
Da Typen vollwertige Werte sind, wird man zur Laufzeit tatsächlich feststellen, dass
Vergleiche wie var int == int? als Resultat true liefern. Trotzdem ist diese
Definition von var• relativ nutzlos, weil der Compiler dynamische Operatoren mit
Implementierung in Typausdrücken nicht akzeptiert (vgl. Abschnitt 2.3) und eine
Deklaration der Art "i" : var int daher fehlerhaft wäre.</p>
          <p>Damit der Operator var• wirklich nützlich ist, muss er wie folgt als virtueller
Operator definiert werden:
Allgemein besitzt die Deklaration eines virtuellen Operators ebenfalls eine
Parameterliste in eckigen Klammern sowie eine anschließende Signatur (im Beispiel "var" T).
Anstelle von Resultattyp und Implementierung folgt dann jedoch eine sog.
Realisierung nach einem Gleichheitszeichen (im Beispiel T?). Der Resultattyp des Operators
ergibt sich implizit aus dem Typ der Realisierung (im Beispiel lautet er type).</p>
          <p>Die Anwendung eines virtuellen Operators unterscheidet sich syntaktisch nicht von
der Anwendung eines anderen Operators. Beispielsweise sind var int und var List
int korrekte Anwendungen des Operators var•, während var 2 fehlerhaft ist, weil
der Operand 2 nicht den geforderten Typ type besitzt.</p>
          <p>Die einzige Besonderheit besteht darin, dass eine Anwendung eines virtuellen
Operators, nachdem sie erfolgreich auf Typkorrektheit überprüft wurde, vom Compiler
sofort durch die Realisierung des Operators ersetzt wird, in der die Parameter des
Operators wiederum durch die entsprechenden Operanden ersetzt werden. Demnach wird
ein Ausdruck wie var int sofort durch die Realisierung T? ersetzt, in der der
Parameter T wiederum durch den Operanden int ersetzt wird, d. h. der endgültige
Ausdruck lautet int?.</p>
          <p>Da diese Ersetzung bereits zur Übersetzungszeit stattfindet, wird eine Deklaration
der Art "i" : var int jetzt vom Compiler akzeptiert, weil der Teilausdruck var int
sofort durch int? ersetzt wird und die Deklaration daher vollkommen
gleichbedeutend mit "i" : int? ist.</p>
          <p>Da var int „in Wirklichkeit“ also int? bedeutet und nur „scheinbar“ etwas anderes
darstellt, wird var• als „virtueller“ Operator und T? als seine „Realisierung“
bezeichnet.</p>
        </sec>
      </sec>
      <sec id="sec-3-2">
        <title>3.2 Automatische Hygiene und bewusste Unhygiene</title>
        <p>Obwohl der oben beschriebene Ersetzungsprozess ähnlich abläuft wie z. B. beim
C/C++-Präprozessor, besteht ein essentieller Unterschied darin, dass die Ersetzung
nicht textuell, sondern strukturell erfolgt. Sowohl die Realisierung des virtuellen
Operators als auch die einzusetzenden Operanden wurden bereits vor der Ersetzung vom
Compiler verarbeitet − und dabei auch auf Typkorrektheit überprüft − und liegen
daher nicht mehr als Folgen von Eingabezeichen oder -symbolen vor, sondern als fertige
Syntaxbäume. Daraus folgt zum einen, dass sich durch den Ersetzungsprozess keine
Operatorbindungen mehr ändern können und damit automatisch „Hygiene“
gewährleistet ist. Eine weitere wichtige Konsequenz ist, dass virtuelle Operatoren bereits
„früh“, d. h. bei ihrer Deklaration, auf Korrektheit geprüft werden können (was bei
C++-Templates beispielsweise nicht möglich ist) und dass eine korrekte Anwendung
eines korrekten virtuellen Operators garantiert wieder einen korrekten Ausdruck als
Ergebnis liefert.</p>
        <p>Im folgenden Beispiel bedeutet Hygiene, dass die Definition des (parameterlosen)
virtuellen Operators printX mit Realisierung print x nur korrekt ist, weil zuvor
eine globale Variable x definiert wurde, und dass eine Anwendung von printX immer
deren Wert ausgibt, selbst wenn sie an der Anwendungsstelle durch ein lokales x
verdeckt wird:</p>
        <p>Globale Variable x mit Wert 1
"x" : int?; x = 1;
"printX" = print x;
let
in
end
["B" : type; "body" : B]
"withX" body "end" =
("x" : int?; x = 2; body);
"x" : int?; x = 1;
withX print x end
["x" : int?]
"printX" = print x
"x" : int?; x = 2</p>
        <sec id="sec-3-2-1">
          <title>Lokale Variable x mit Wert 2</title>
          <p>printX;
print x</p>
        </sec>
        <sec id="sec-3-2-2">
          <title>Ausgabe: 1</title>
        </sec>
        <sec id="sec-3-2-3">
          <title>Ausgabe: 2</title>
          <p>Im nächsten Beispiel bedeutet Hygiene, dass die in der Realisierung des Operators
withX•end definierte lokale Variable x keinerlei Einfluss auf die Interpretation des
Namens x in dessen Operanden hat, d. h. bei einer Anwendung des Operators wird x
immer im Kontext dieser Anwendung interpretiert:</p>
        </sec>
        <sec id="sec-3-2-4">
          <title>Lokale Variable x mit Wert 2</title>
        </sec>
        <sec id="sec-3-2-5">
          <title>Globale Variable x mit Wert 1</title>
        </sec>
        <sec id="sec-3-2-6">
          <title>Ausgabe: 1</title>
          <p>Wenn printX tatsächlich ein lokal definiertes x ausgeben soll − das dann auch bei
jeder Anwendung ein anderes sein kann − , kann man dieses als impliziten Parameter
übergeben (was an sich nichts mit virtuellen Operatoren zu tun hat, d. h. man könnte
printX genausogut als gewöhnlichen Operator definieren):
Und wenn das in der Realisierung von withX•end definierte x tatsächlich in seinen
Operanden sichtbar und verwendbar sein soll, kann man dies durch eine spezielle
Sichtbarkeitsdeklaration body :+ ^^ erreichen, die bewirkt, dass während der
Verarbeitung des zum Parameter body gehörenden Operanden alle Operatoren sichtbar
sind, die an der Verwendungsstelle von body in der Realisierung des Operators
sichtbar sind (wenn man diese gedanklich an die Anwendungsstelle des Operators setzt):
["B" : type; "body" : B; body :+ ^^]
"withX" body "end" =
("x" : int?; x = 2; body)
Auf diese Weise kann man also gezielt „unhygienische“ Operatoren definieren, deren
lokale Operatoren an ihrer Verwendungsstelle bewusst sichtbar sein sollen, was in
bestimmten Fällen durchaus erwünscht sein kann (siehe Abschnitt 3.5).</p>
        </sec>
      </sec>
      <sec id="sec-3-3">
        <title>3.3 Ermittlung des Typs eines Ausdrucks</title>
        <p>Der folgende virtuelle Operator typeof•end akzeptiert einen Operanden x mit
beliebigem Typ T, wobei der Wert von T aus dem Typ des jeweils vorliegenden Operanden
deduziert wird:
["T" : type; "x" : T]
"typeof" x "end" = T
Demnach sind typeof 1 + 2 end und</p>
        <p>typeof ["n" : int] n "2" : int { n * n } end
korrekte Anwendungen des Operators. Beim zweiten Beispiel ist zu beachten, dass
eine Deklaration ebenfalls ein Ausdruck ist, der als Resultat den deklarierten Operator
liefert und demnach als Typ den entsprechenden Operatortyp besitzt.</p>
        <p>Da typeof•end ein virtueller Operator ist, werden seine Anwendungen sofort durch
seine Realisierung T ersetzt, in der der Parameter T wiederum durch den
entsprechenden Operanden ersetzt wird. Da 1 + 2 den Typ int besitzt, wird T bei der Anwendung
typeof 1 + 2 end mit int belegt, sodass der gesamte Ausdruck letztlich durch int
ersetzt wird. Demnach wäre die Deklaration "x" : typeof 1 + 2 end ? vollkommen
gleichbedeutend mit "x" : int?, auch wenn erstere in diesem Beispiel nicht
besonders sinnvoll ist.</p>
        <p>Die Anwendung von typeof•end auf eine Deklaration kann jedoch dazu verwendet
werden, den Typ des deklarierten Operators zu ermitteln − was auf andere Weise
nicht möglich ist − , und damit z. B. eine Variable zu deklarieren, in der Operatoren
mit diesem Typ gespeichert werden können:</p>
        <p>"x" : typeof ["n" : int] n : int {} end ?
Da der Name eines Operators und der Inhalt seiner Implementierung keinen Einfluss
auf seinen Typ haben, kann man auf den Namen in diesem Fall auch verzichten (die
Signatur besteht nur aus dem Parameter n) und die Implementierung leer lassen.
(Zumindest eine leere Implementierung muss jedoch vorhanden sein, damit es sich um
einen dynamischen Operator handelt, dessen Typ verschieden ist vom Typ eines
ansonsten gleichartigen statischen Operators ohne Implementierung.)
Da das mehrmalige Hinschreiben eines solchen Operatortyps natürlich umständlich
ist, kann man ihn wiederum durch einen (in diesem Fall parameterlosen) virtuellen
Operator abkürzen, z. B.:
"Int2Int" = typeof ["n" : int] n : int {} end;
"x" : Int2Int?
Um an die Variable x einen konkreten Operator zuzuweisen, kann man wiederum
ausnutzen, dass eine Deklaration den deklarierten Operator als Resultat liefert:
x = ["n" : int] n "2" : int { n * n }
Tatsächlich gibt es in MOSTflexiPL keine andere Möglichkeit, Operatortypen zu
konstruieren. Die aus anderen Sprachen bekannte Syntax P −&gt; R, P1 −&gt; P2 −&gt; R o. ä.
würde zwar für einfache Fälle ausreichen, aber für Operatoren mit deduzierten
Parametern (deren Typ nicht immer type sein muss) und impliziten Parametern (bei
denen nicht nur ihr Typ, sondern auch ihre Syntax relevant ist), bräuchte man wesentlich
komplexere Ausdrucksmöglichkeiten, die i. w. analog zu Deklarationen aufgebaut sein
müssten. Daher ist die Anwendung des (vordefinierten) Operators typeof•end auf
eine entsprechende „Musterdeklaration“ letztlich einfacher und sprachtheoretisch
„ökonomischer“. Allerdings spricht nichts dagegen, für einfache Fälle beispielsweise
die bekannte Pfeilsyntax wiederum durch einen virtuellen Operator zu definieren:
["P" : type; "R" : type]
P "−&gt;" R =
typeof ["x" : P] x : R {} end;
"Int2Int" = int −&gt; int</p>
      </sec>
      <sec id="sec-3-4">
        <title>3.4 Benutzerdefinierte Deklarationsoperatoren</title>
        <p>Eine weitere Kategorie von Operatoren, die virtuell definiert werden müssen, damit
sie den gewünschten Effekt haben, sind Deklarationsoperatoren, d. h. Operatoren, bei
deren Anwendung andere Operatoren deklariert werden.</p>
        <p>Wenn man beispielsweise Variablen (ähnlich wie in C, C++ und Java) in der Form
int "i" anstelle von "i" : int? deklarieren möchte, kann man hierfür den
folgenden virtuellen Operator •• verwenden:
["T" : type; "name" : string]
T name =
name : T?
Eine Anwendung wie z. B. int "i" (die nur aus Operanden besteht, weil der
Operator keine Namen besitzt) wird wiederum durch die Realisierung des Operators, d. h.
durch den Ausdruck name : T? ersetzt, in dem die Parameter name und T durch die
Operanden "i" bzw. int ersetzt werden, sodass schließlich der Ausdruck "i" :
int? entsteht.</p>
        <p>Um Variablen bei ihrer Deklaration sofort initialisieren zu können, z. B. int
"i" = 1, kann man einen weiteren Operator ••=• definieren:
["T" : type; "name" : string = "var"; "init" : T]
T name "=" init =
(name : T?; var = init)
Damit der Compiler die Realisierung dieses Operators korrekt verarbeiten kann, ist
jedoch eine Sonderregel erforderlich, weil die Deklaration name : T? eigentlich eine
Variable mit unbekanntem Namen deklariert − name steht hier nicht wie sonst in
Anführungszeichen, weil es ja durch den vom Programmierer gewünschten Namen wie
z. B. "i" ersetzt werden soll, der zu diesem Zeitpunkt aber noch nicht bekannt ist und
außerdem bei jeder Anwendung des Operators verschieden sein kann. Trotzdem soll
diese unbekannte Variable in der anschließenden Zuweisung var = init bereits
verwendet werden. Um dies zu ermöglichen, kann man bei der Deklaration des
Parameters name einen Ersatzwert angeben (hier "var") und sich vorstellen, dass alle
Verwendungen von name in der Realisierung des virtuellen Operators vorübergehend
durch diesen ersetzt werden, so wie wenn name selbst ein virtueller Operator mit
Realisierung "var" wäre. Das hat zur Folge, dass die Deklaration name : T? dann
vorübergehend "var" : T? lautet (mit var in Anführungszeichen) und die deklarierte
Variable daher den Namen var erhält, mit dem sie anschließend verwendet werden
kann. Bei einer späteren Anwendung des virtuellen Operators wird name dann aber
wieder durch den tatsächlich angegebenen Namen ersetzt.</p>
      </sec>
      <sec id="sec-3-5">
        <title>3.5 Komfortablere Zählschleife</title>
        <p>Die Verwendung des in Abschnitt 2.4 definierten Operators for•=•..•do•end ist
noch nicht maximal komfortabel, weil die Laufvariable vom Programmierer explizit
deklariert werden muss. Wenn der Operator die Variable selbst deklarieren würde,
müsste man als Programmierer nur noch den gewünschten Namen angeben, z. B.:
for "i" = 1 .. 10 do</p>
        <p>print i2
end
Mit den zuvor beschriebenen Möglichkeiten kann der hierfür benötigte Operator wie
folgt definiert werden:
[
)
"name" : string = "var"; "lower" : int; "upper" : int;
"B" : type; "body" : B; body :+ ^^
]
"for" name "=" lower ".." upper "do" body "end" = (
name : int?;
var = lower;
while var &lt;= upper do
body;
var = var + 1
end
Genauso wie beim Operator ••=•, kann die durch name : int? deklarierte
„unbekannte“ Variable anschließend mit dem bei der Deklaration des Parameters name
angegebenen Ersatznamen var verwendet werden, um ihren Wert zuzuweisen und
abzufragen. Bei einer späteren Anwendung des virtuellen Operators mit einem konkreten
Variablennamen, z. B. for "i" = 1 .. 10 do print i2 end, erhält die Variable dann
ihren „richtigen“ Namen i. Damit diese Variable auch im Schleifenrumpf (im
Beispiel print i2) verwendet werden kann, wird sie mittels body :+ ^^ im
entsprechenden Operanden sichtbar gemacht (vgl. die Erläuterungen am Ende von Abschnitt 3.2).</p>
      </sec>
    </sec>
    <sec id="sec-4">
      <title>4 Klassendefinitionen wie in Java</title>
      <p>Als abschließendes, komplexeres Beispiel soll im folgenden ein Operator class•{•}
mit einigen Hilfsoperatoren definiert werden, der z. B. wie folgt verwendet werden
kann, um eine Klasse Account ähnlich wie in Java zu definieren (balance bezeichne
den Kontostand in Cent):
class "Account" {
int "number";
int "balance" = 0;
}
int "deposit" (int "amount") { balance = balance + amount };
int "withdraw" (int "amount") { deposit(−amount) }</p>
      <sec id="sec-4-1">
        <title>4.1 Exemplarische Transformation einer Klasse</title>
        <p>Die entscheidende Frage ist hier zunächst, wie eine solche Klasse in
MOSTflexiPLDeklarationen transformiert werden kann, damit sie möglichst wie in Java verwendet
werden kann, z. B.:</p>
        <p>Account "a" = new Account();
a.number = 4711;
a.deposit(1000);
a.withdraw(500)
"Account" : type;
Da jede Klasse in Java einen eindeutigen Typ repräsentiert, muss dieser als erstes
definiert werden (vgl. Abschnitt 2.3):
Um später Objekte der Klasse durch den Ausdruck new Account() erzeugen zu
können, wird ein entsprechender MOSTflexiPL-Operator gebraucht, der ein neues Objekt
this des Typs Account erzeugt und zurückliefert (vgl. ebenfalls Abschnitt 2.3):
"new" "Account" "(" ")" : Account
{ "this" : Account; this }
Wenn man nun noch den in Abschnitt 3.4 definierten Operator ••=• hinzunimmt,
„funktioniert“ bereits die Anweisung:</p>
        <p>Account "a" = new Account()
Damit eine Objektvariable (instance variable) wie z. B. int "number" in der
gewohnten Form a.number verwendet werden kann, kann man sie wie in Abschnitt 2.3
auf eine parametrisierte Variablendeklaration abbilden, die jedem Account-Objekt
this eine eindeutige int-Variable this.number zuordnet:
["this" : Account]
this "." "number" : int?
Um Objektvariablen innerhalb ihrer Klasse − genauer, an Stellen, an denen ein
„aktuelles Objekt“ this mit passendem Typ zur Verfügung steht − auch ohne explizite
Angabe eines Objekts verwenden zu können, kann man jeweils einen weiteren Operator
definieren, bei dem this kein expliziter, sondern ein impliziter Parameter ist, der
vom Compiler automatisch übergeben wird (vgl. Abschnitt 3.2):
["this" : Account]
"number" : int?
{ this.number }
Damit ist number an den entsprechenden Stellen äquivalent zu this.number.
Um eine Objektmethode (instance method) ähnlich verwenden zu können wie eine
Objektvariable, z. B. a.deposit(1000), kann man ihre Definition</p>
        <p>int "deposit" (int "amount") { balance = balance + amount }
wie folgt auf einen MOSTflexiPL-Operator abbilden:
["this" : Account; "amount" : int]
this "." "deposit" "(" amount ")" : int
{ balance = balance + amount }
Und auch hier kann ein zusätzlicher Operator mit implizitem Parameter this dafür
sorgen, dass Methoden innerhalb ihrer Klasse auch ohne explizite Angabe eines
Aufrufobjekts verwendet werden können, z. B. deposit(−amount):
["this" : Account; "amount" : int]
"deposit" "(" amount ")" : int
{ this.deposit(amount) }
Wenn eine Objektvariable eine Initialisierung besitzt, z. B. int "balance" = 0, dann
muss jeweils bei der Erzeugung eines neuen Objekts der Klasse eine entsprechende
Zuweisung ausgeführt werden. Dies kann wie folgt durch eine Reimplementierung
des Operators new Account ( ) erreicht werden, die sich durch das vorangestellte
Ausrufezeichen von einer normalen Operatordeklaration unterscheidet:
! "new" "Account" "(" ")" : Account
{
"this" : Account = ! new Account ( );
balance = 0;
this
}
Diese neue Implementierung des Operators überschreibt global die ursprüngliche
bzw. vorige Implementierung, d. h. Anwendungen des Operators werden entsprechend
„umgeleitet“. Sie kann die vorige Implementierung jedoch − wieder mit einem
vorangestellten Ausrufezeichen − aufrufen.</p>
      </sec>
      <sec id="sec-4-2">
        <title>4.2 Allgemeine Definition der Transformation</title>
        <p>Nach diesen Vorüberlegungen kann man den virtuellen Operator class•{•} nun wie
in Abbildung 1 deklarieren. Als Parameter erhält er den Namen cname der Klasse als
string sowie einen Klassenrumpf cbody mit beliebigem Typ B.</p>
        <p>In der Realisierung des Operators wird durch die Deklaration cname : type zunächst
ein neuer Typ mit unbekanntem Namen definiert, der bei einer späteren Anwendung
des Operators durch einen konkreten Namen (z. B. Account) ersetzt wird.
Anschließend kann dieser Typ jedoch mit dem Ersatznamen c verwendet werden.
Durch die Deklaration "new" cname "(" ")" : c { ...... } wird als nächstes der
Operator zur Erzeugung von Objekten der Klasse definiert. Man beachte hierbei die
unterschiedlichen Bedeutungen von cname und c: Der Parameter cname wird später
durch den Namen der Klasse (in Anführungszeichen) ersetzt, während c den gerade
definierten Typ bezeichnet.</p>
        <p>Damit die Definitionen von Objektvariablen und -methoden im Rumpf der Klasse wie
oben beschrieben auf geeignete MOSTflexiPL-Operatordeklarationen abgebildet
werden, werden im ersten Operanden von let•in•end (vgl. Abschnitt 2.5) die hierfür
erforderlichen Hilfsoperatoren wiederum als virtuelle Operatoren definiert, die somit
nur im zweiten Operanden von let•in•end, d. h. an der Verwendungsstelle von
cbody sichtbar sind. Die spezielle Sichtbarkeitsdeklaration cbody :+ ^^ (vgl.
Abschnitt 3.2) in der Parameterliste des Hauptoperators class•{•} bewirkt jedoch, dass
diese Hilfsoperatoren im entsprechenden Operanden sichtbar werden und somit
bereits während der Verarbeitung des Klassenrumpfs vom Compiler angewandt werden.
Der erste dieser Hilfsoperatoren (mit der Syntax ••) transformiert einen Ausdruck
wie z. B. int "number" (innerhalb einer Klassendefinition class "Account"
{ ...... }) in die Deklarationen von •.number (mit einem expliziten
Account-Parameter this) und number (mit einem entsprechenden impliziten Parameter). Der
zweite Hilfsoperator (mit der Syntax ••(••){•}) ist in ähnlicher Weise für die
Transformation von Methodendefinitionen zuständig. Der dritte Hilfsoperator
schließlich (mit der Syntax ••=•) transformiert eine Objektvariable mit Initialisierung auf
die gleiche Weise wie eine Objektvariable ohne Initialisierung (indem er am Anfang
seiner Realisierung den ersten Hilfsoperator anwendet) mit einer zusätzlichen
Reimplementierung des Operators zur Objekterzeugung.</p>
      </sec>
      <sec id="sec-4-3">
        <title>4.3 Ausblick</title>
        <p>Natürlich kann eine Operatordefinition, die sich auf einer einzigen Buchseite
abdrucken lässt, bei weitem nicht alle Aspekte von Java-Klassen berücksichtigen.
Beispielsweise fehlen im Moment Konstruktoren, statische Variablen und Methoden,
Zugriffsrechte und natürlich Vererbung. Die Berücksichtigung der meisten dieser Dinge
ist jedoch eine reine „Fleißarbeit“, die man durch geeignete Deklarationen weiterer
Hilfsoperatoren erledigen kann. Die grundsätzliche Idee, wie man private von
öffentlichen Bestandteilen trennen kann, wurde in Abschnitt 2.5 (Operator let•in•end)
be["cname" : string = "c"; "B" : type; "cbody" : B; cbody :+ ^^]
"class" cname "{" cbody "}" = (
cname : type;
"new" cname "(" ")" : c
{ "this" : c; this };
let
["T" : type; "vname" : string = "v"]
T vname = (
["this" : c]
this "." vname : T?;
["this" : c]
vname : T?
{ this.v }
);
[
"R" : type; "mname" : string = "m";
"P" : type; "pname" : string = "p";
"mbody" : R; mbody :+ ^^
]
R mname "(" P pname ")" "{" mbody "}" = (
["this" : c; pname : P]
this "." mname "(" p ")" : R { mbody };
["this" : c; pname : P]
mname "(" p ")" : R { this.m(p) }
);
["T" : type; "vname" : string = "v"; "init" : T]
T vname "=" init = (</p>
        <p>T vname;
! "new" cname "(" ")" : c
{
"this" : c = ! new c ( );
v = init;
this
)</p>
        <p>}
Abbildung 1: Virtueller Operator class•{•} mit zugehörigen Hilfsoperatoren
schrieben. Vererbungsbeziehungen können ähnlich wie in [He07] auf bidirektionale
Beziehungen und implizite Typumwandlungen abgebildet werden.</p>
      </sec>
    </sec>
    <sec id="sec-5">
      <title>5 Diskussion</title>
      <p>Ein wesentliches Ziel bei der Konzeption von MOSTflexiPL besteht darin,
syntaktische Erweiterbarkeit nicht als „Anhängsel“ anzubieten, sondern als das fundamentale
Kernkonzept der Sprache. Syntaktische Erweiterungen sollen mit den gleichen
Sprachmitteln − und genauso „leicht“ − formulierbar sein wie „normale“ Programme.
Dies wird im wesentlichen durch das sehr allgemeine Konzept von Operatoren
erreicht, das Konstanten, Variablen, Funktionen, einfache Operatoren, mehrteilige
Operatorkombinationen, Kontrollstrukturen, Typkonstruktoren und Deklarationsformen −
kurz, alle wesentlichen Bestandteile einer Programmiersprache − subsumiert. Jede
Operatordeklaration stellt „nebenbei“ eine Syntaxerweiterung dar, ohne dass hierfür
ein spezieller Zusatzmechanismus wie z. B. ein Makrosystem benötigt wird.</p>
      <p>Zur Erreichung dieses Ziels sind aber auch Details wichtig, wie z. B. die explizite
Unterscheidung zwischen Konstanten und Variablen, die es ermöglicht, Variablen
anstelle ihres Werts an einen Operator zu übergeben, sowie die Möglichkeit, Operanden
als unausgewertete Ausdrücke zu übergeben. Beides wird häufig bei der Definition
neuer Kontrollstrukturen benötigt (wie z. B. beim Operator for•=•..•do•end in
Abschnitt 2.4).</p>
      <p>Trotzdem stößt man auch mit diesem sehr allgemeinen Operatorkonzept früher oder
später an Grenzen, wie die Beispiele in Abschnitt 3 verdeutlichen, die sich wohl
prinzipiell nur durch syntaktische Transformationen zur Übersetzungszeit überwinden
lassen. Virtuelle Operatoren stellen daher eine möglichst kleine Erweiterung des
ursprünglichen Operatorkonzepts dar, mit dem sich die identifizierten Probleme
möglichst gut lösen lassen und das sich nahtlos in das vorhandene Konzept einfügt.
Wie bereits erwähnt, finden die durch virtuelle Operatoren definierten
Syntaxtransformationen nicht textuell (wie z. B. beim C/C++-Präprozessor), sondern rein strukturell
statt, nachdem alle beteiligten Teilausdrücke (d. h. die Realisierung des Operators
sowie die konkreten Operanden einer Operatoranwendung) vom Compiler bereits
verarbeitet und auf Typkorrektheit überprüft wurden und dabei sämtliche Namen an die
jeweils lexikalisch sichtbaren Operatoren gebunden wurden. Auf diese Weise wird
automatisch Hygiene gewährleistet, ohne dass man sich als Programmierer darüber
Gedanken machen oder hierfür Hilfsmittel wie gensym in Common Lisp [St90]
einsetzen müsste. Aber auch „unter der Oberfläche“ sind keinerlei zusätzliche Maßnahmen
wie z. B. Markierungen und Namensersetzungen in Scheme [Sp10] erforderlich, um
Hygiene zu gewährleisten. Auch die Regeln, wann und wie welche Teile eines
Syntaxbaums durch etwas anderes ersetzt werden, sind im Vergleich zu Scheme
vergleichsweise einfach.</p>
      <p>Da Hygiene manchmal auch unerwünscht ist, gibt es in MOSTflexiPL zwei
Mechanismen, um sie zu umgehen: Durch implizite Parameter (die an sich nichts mit
virtuellen Operatoren zu tun haben) können in der Implementierung oder Realisierung eines
Operators Namen verwendet werden, die an seiner Verwendungsstelle sichtbar sind.
Mit Hilfe des Sonderoperators ^^ in der Importdeklaration eines Parameters können
umgekehrt Operatoren, die in der Realisierung eines virtuellen Operators definiert
werden (und deren Namen meist von anderen Parametern dieses Operators abhängen),
in seinen Operanden (d. h. an der Verwendungsstelle des virtuellen Operators)
sichtbar gemacht werden.</p>
      <p>Wenn man in Scheme die dort ebenfalls automatisch gewährleistete Hygiene
durchbrechen will, muss man entweder Makros im Stil von Common Lisp (typischerweise
mit quote und unquote) verwenden − sofern diese von der jeweiligen
Implementierung unterstützt werden − oder auf syntax−&gt;datum zurückgreifen, für dessen
Verwendung jedoch relativ detaillierte Kenntnisse über den Makroersetzungsprozess
notwendig sind.</p>
      <p>In vielen Sprachen oder Systemen, die syntaktische Erweiterungen in der einen oder
anderen Form unterstützen, z. B. Common Lisp, Scheme, Dylan [Cr97] und Java
Syntactic Extender (JSE) [BP01], kann der Compiler Makroanwendungen rein
strukturell verarbeiten, ohne in diesem Moment bereits alle Details zu „verstehen“, indem
er einen sog. „skeletal parse“ durchführt. Essentielle Voraussetzung hierfür ist eine
gewisse syntaktische Grundstruktur der Sprache oder, weniger freundlich
ausgedrückt, ein syntaktisches „Korsett“, aus dem man auch durch Syntaxerweiterungen
nicht herauskommt (z. B. geklammerte Ausdrücke in Common Lisp und Scheme oder
Grundregeln für die Struktur von Anweisungen in Dylan und JSE).</p>
      <p>Im Gegensatz dazu, bietet MOSTflexiPL nahezu unbegrenzte syntaktische
Gestaltungsmöglichkeiten − mit der Konsequenz, dass der Compiler bei der Verarbeitung
eines Teilausdrucks (z. B. print i2) alle darin verwendeten Operatoren exakt kennen
muss. Wenn die Zählvariable einer Schleife z. B. i2 hieße (was prinzipiell möglich,
wenn auch nicht unbedingt empfehlenswert ist), dann wäre i2 keine Anwendung des
Operators •2 auf die Variable i (die es in diesem Fall vermutlich gar nicht gibt),
sondern lediglich die Verwendung der Variable i2.</p>
      <p>Ein weiterer wichtiger Unterschied zwischen virtuellen Operatoren in MOSTflexiPL
und „klassischen“ Makros à la Common Lisp und Scheme besteht darin, dass letztere
bei Bedarf die gesamte Ausdrucksmächtigkeit der Sprache zur Übersetzungs- bzw.
„Ersetzungszeit“ verwenden können, während MOSTflexiPL aus zwei Gründen
bewusst kein solches „prozedurales“ Makrosystem anbietet: Erstens wurde es bisher
schlicht und einfach nicht gebraucht, zweitens ist nicht klar, wie es sich mit der in
Abschnitt 3.2 beschriebenen „frühen“ statischen Überprüfung virtueller Operatoren
vereinbaren ließe. Außerdem besteht natürlich die Gefahr, dass der für Transformationen
ausgeführte Benutzercode Endlosschleifen oder -rekursionen enthält und der
Compiler daher nicht terminiert oder sogar abstürzt.</p>
    </sec>
    <sec id="sec-6">
      <title>6 Zusammenfassung und Ausblick</title>
      <p>Virtuelle Operatoren stellen eine kleine, aber wichtige Erweiterung des allgemeinen
Operatorkonzepts von MOSTflexiPL dar und fügen sich nahtlos in dieses ein. Sie
ermöglichen Syntaxerweiterungen für „Fortgeschrittene“, die sich nur durch
Transformationen zur Übersetzungszeit mit der strengen statischen Typisierung von
MOSTflexiPL in Einklang bringen lassen. Trotzdem unterscheidet sich die Definition
virtueller Operatoren nicht wesentlich von der Definition gewöhnlicher Operatoren.
Insbesondere werden keine Spezialkonstrukte wie quote und unquote in Common Lisp,
code quotes in JSE oder patterns und rewrite rules in Dylan benötigt.
Nichtsdestotrotz ist auch in MOSTflexiPL die Definition komplexer
Syntaxerweiterungen manchmal noch etwas mühsam. Beispielsweise fehlt noch eine Möglichkeit,
Operatoren mit optionalen und wiederholten Bestandteilen wie z. B.
try-catchfinally oder die Transformation von Java-Methoden mit beliebig vielen Parametern
bequem zu definieren.</p>
      <p>Die in Abschnitt 1 und 4.3 erwähnten benutzerdefinierbaren impliziten
Typumwandlungen sind zwar prinzipiell vorhanden, müssen aber im Detail noch konzeptuell
verbessert werden. Eine weitere große „Baustelle“ ist nach wie vor die Ausgabe
sinnvoller und hilfreicher Fehlermeldungen zur Übersetzungszeit sowie die Fortsetzung
des Übersetzungsvorgangs nach einem Fehler. Außerdem muss für einen produktiven
Einsatz von MOSTflexiPL einerseits die Effizienz des Compilers noch deutlich
verbessert werden und andererseits das im Namen der Sprache bereits verankerte, aber
momentan noch nicht verfügbare Modulkonzept implementiert werden.</p>
    </sec>
    <sec id="sec-7">
      <title>Literaturverzeichnis</title>
    </sec>
  </body>
  <back>
    <ref-list>
      <ref id="ref1">
        <mixed-citation>
          [BP01]
          <string-name>
            <given-names>J.</given-names>
            <surname>Bachrach</surname>
          </string-name>
          ,
          <string-name>
            <surname>K.</surname>
          </string-name>
          <article-title>Playford: “The Java Syntactic Extender (JSE)</article-title>
          .
          <source>” In: Proc. 2001 ACM SIGPLAN Conf. on Object-Oriented Programming, Systems, Languages and Applications (OOPSLA '01)</source>
          (Tampa Bay, FL,
          <year>October 2001</year>
          ),
          <fpage>31</fpage>
          −
          <lpage>42</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref2">
        <mixed-citation>
          [Cr97]
          <string-name>
            <given-names>I. D.</given-names>
            <surname>Craig</surname>
          </string-name>
          : Programming in Dylan. Springer-Verlag, London,
          <year>1997</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref3">
        <mixed-citation>
          [He05]
          <string-name>
            <given-names>C.</given-names>
            <surname>Heinlein</surname>
          </string-name>
          <article-title>: “Global and Local Virtual Functions in C++</article-title>
          .
          <source>” Journal of Object Technology</source>
          <volume>4</volume>
          (
          <issue>10</issue>
          )
          <year>December 2005</year>
          ,
          <fpage>71</fpage>
          −
          <lpage>93</lpage>
          , http://www.jot.fm/issues/ issue_2005_
          <volume>12</volume>
          /article4.
        </mixed-citation>
      </ref>
      <ref id="ref4">
        <mixed-citation>
          [He07]
          <string-name>
            <given-names>C.</given-names>
            <surname>Heinlein</surname>
          </string-name>
          <article-title>: “Open Types and Bidirectional Relationships as an Alternative to Classes and Inheritance</article-title>
          .
          <source>” Journal of Object Technology</source>
          <volume>6</volume>
          (
          <issue>3</issue>
          ) March/April 2007,
          <fpage>101</fpage>
          −
          <lpage>151</lpage>
          , http://www.jot.fm/issues/issue_2007_
          <volume>03</volume>
          /article3.
        </mixed-citation>
      </ref>
      <ref id="ref5">
        <mixed-citation>
          [He12]
          <string-name>
            <given-names>C.</given-names>
            <surname>Heinlein: “MOSTflexiPL − Modular</surname>
          </string-name>
          , Statically Typed,
          <article-title>Flexibly Extensible Programming Language</article-title>
          .” In: J.
          <string-name>
            <surname>Edwards</surname>
          </string-name>
          (ed.)
          <source>: Proc. ACM Int. Symp. on New Ideas, New Paradigms, and Reflections on Programming and Software (Onward!</source>
          <year>2012</year>
          )
          <article-title>(Tucson</article-title>
          ,
          <string-name>
            <surname>AZ</surname>
          </string-name>
          ,
          <year>October 2012</year>
          ),
          <fpage>159</fpage>
          −
          <lpage>178</lpage>
          .
        </mixed-citation>
      </ref>
      <ref id="ref6">
        <mixed-citation>
          <string-name>
            <surname>[Sp10] M. Sperber</surname>
            ,
            <given-names>R. K.</given-names>
          </string-name>
          <string-name>
            <surname>Dybvig</surname>
            ,
            <given-names>M.</given-names>
          </string-name>
          <string-name>
            <surname>Flatt</surname>
            ,
            <given-names>A. v.</given-names>
          </string-name>
          <string-name>
            <surname>Straaten</surname>
            ,
            <given-names>R.</given-names>
          </string-name>
          <string-name>
            <surname>Findler</surname>
          </string-name>
          , J. Matthews (eds.):
          <source>Revised [6] Report on the Algorithmic Language Scheme</source>
          . Cambridge University Press,
          <year>2010</year>
          .
        </mixed-citation>
      </ref>
      <ref id="ref7">
        <mixed-citation>
          [St90]
          <string-name>
            <given-names>G. L. Steele</given-names>
            <surname>Jr.: Common Lisp: The Language (Second Edition). Digital</surname>
          </string-name>
          Press, Bedford, MA,
          <year>1990</year>
          .
        </mixed-citation>
      </ref>
    </ref-list>
  </back>
</article>