Perl 6

Die Zukunft des Programmierens

Spracherweiterungen

Die Erfahrung zeigt, dass sich viele konkrete Design-Entscheidungen bei der Entwicklung einer Programmiersprache irgendwann in irgend einer Form als falsch herausstellen.

Deshalb wurde Perl 6 so entwickelt, dass möglichst vieles möglichst leicht änderbar ist und auch erweiterbar ist.

Hier werden einige Möglichkeiten vorgestellt, wie man Perl 6 verändern und erweitern kann.

Neue Operatoren

Operatoren sind in Perl 6 nur Funktionen mit komischen Namen. In dem Ausdruck 1 + 2 steht das + für infix:<+>.

Im Allgemeinen hat ein Operator einen Typ (im obigen Beispiel infix), ein Symbol (+), einen Vorrang (Englisch precedence), eine Assoziativität und eine Signatur.

Operatortypen

Perl 6 kennt folgende Operatortypen:

Name            Beispiel    Beschreibung

infix           1 + 2       steht zwischen zwei Ausdrücken
postfix         $a++        steht hinter einem Ausdruck
prefix          +3          steht vor einem Ausdruck
circumfix       <a b c>     umschliesst einen Ausdruck
postcircumfix   uc($str)    steht hinter einem Ausdruck und um-
                            schliesst einen weiteren Ausdruck

Zwischen einem Ausdruck und dem entpsrechenden postfix- prefix- oder postcircumfix-Operator darf kein Leerzeichen stehen.

Die ersten vier Operatoren sind immer subs, postcircumfix-Operatoren sind Methoden (d.h. gehören zu einer Klasse).

Symbole

Symbole für Operatoren sind nicht auf Sonderzeichen beschränkt, und auch die Länge ist nicht beschränkt. So könnte man sich einen Operator mit Namen kaffee definieren (was aber nicht besonders sinnvoll wäre).

Beim Parsen gilt das sogenannte "Longest Token Matching", d.h. wenn an einer Stelle mehrere Operatoren passen, wird der längste gewählt. Deshalb wird ++$a als prefix:'++'($a) geparsed und nicht als prefix:'+'(prefix:'+'($a)).

Operatorvorrang

Operatorvorrang entscheidet, wie "gierig" ein Operator ist, und in welcher Reihenfolge Operatoren ausgeführt werden.

In dem Ausdruck 1 + 2 * 3 wird erst das 2 * 3 ausgeführt, danach wird 1 addiert, weil infix:'*' einen höheren Vorrang als infix:'+' hat.

Wenn man neue Operatoren definiert, kann man ihnen eine bestimmten Vorrang mitgeben. Dieser wird relativ zu bisherigen Operatoren angegeben.

multi sub postfix:<!> is equiv(&postfix:<++>) (Int $a) {
    return [*] 1..$a;
}

Mit is equiv gibt man an, dass der neue Operator den gleichen Vorrang (und die gleiche Assoziativität) wie der danach genannte Operator haben soll. Alternativen sind is tighter und is looser, die jeweils neue Vorrangebenen einführen.

Assoziativität

Die Assoziatitvität eines Infixoperators bestimmt, wie ein Konstrukt aus mehreren Operatoren mit der gleichen Vorrangebene gehandhabt wird.

Wenn man z.B. 1 op 2 op 3 hat, und op links-assoziativ ist, wird das ganze genauso wie (1 op 2) op 3 gehandhabt. Wenn die Assoziativität allerdings Rechts ist, wird das Beispiel als 1 op (2 op 3) verstanden. Ein Beispiel, wo das einen Unterschied macht, ist die Division. 1 / (2 / 2) == 1, während (1 / 2) / 2 == 1/4 ist. Daher ist der Ausdruck 1 / 2 / 2 nur dann eindeutig definiert, wenn man die Assozitivität von infix:'/' kennt.

Neben den gerade vorgestellten Assozitivitäten left (links) und right (rechts) gibt es auch noch non (keine Assoziativität; damit wäre das Beispiel 1 op 2 op 3 ein Syntaxfehler), chain (was "chained comparisons" wie in if 0 <= $angle < 2 * pi { ... } erlaubt) und list (wie es z.B. der Komma-Operator zum konstruieren von Listen hat).

Ist die Assoziativität eines Operators nicht angegeben, wird Linksassoziativität angenommen, ausser wenn der Vorrang mit is equiv spezifiert wurde; dann wird die Assoziativität des als äquivalent angegeben Operators übernommen.

Signatur

Die Signatur einer Funktion, einer Methode oder eines Operators gibt an, wie viele Argumente sie oder er bekommt, und optional zu welchen Typs sie sind.

Dabei gilt, dass Pre- und Postfixoperatoren immer ein Argument bekommen, Infixoperatoren der Assoziativitäten left, right, non, chain zwei Argument und Infix-Operatoren der Assozitivität list sowei Circumfix- und Postcircumfixoperatoren Listen erhalten. Postfixoperatoren bekommen ausserdem noch den Ausdruck auf der linken Seite als self übergeben.

Mit den Typangaben kann man, in Verbindung mit Multi Dispatch, Verfeinerungen bestehender Operatoren definieren. Wenn man zum Beispiel eine eigene Subklasse von Str schreibt, kann man sich die String-Konkatenation für Objekte dieser Klasse überschreiben:

class MyStr is Str {
    multi sub *infix:<~>(MyStr $a, Str $b) { ... }
    multi sub *infix:<~>(Str $a, MyStr $b) { ... }

    # oder als eine Subroutine mit zwei möglichen 
    # Signaturen geschrieben:
    multi sub *infix:<~>(MyStr $a, Str $b)|(Str $a, MyStr $b) { ... }

}

Scoping

Per Default sind neue Operatoren nur in dem lexikalischen Scope gültig, in dem sie definiert wurden. Um sie global zu Verfügung zu stellen, kann man ihrem Namen vorne ein Stern * anhängen.

Zusammenfassung

Mit den hier vorgestellten Techniken hat man volle Kontrolle über neue Operatoren, und kann alte verändern. Tatsächlich benutzen alle in Perl 6 eingebaute Operatoren auch diese Techniken, um ihr Verhalten zu definieren.

Für viele neue Operatoren sollte es jedoch reichen, die Vorrangebene anzugeben.

Ein Wort der Warnung ist noch angebracht: neue Operatoren sollten spärlich verwendet werden, und sollten wohl durchdacht sein. Auch muss man vorher den bisherigen Sprachumfang von Perl 6 gut kennen, um keine schwer lesbaren oder unerwarteten Effekte durch das versehentliche Recyclen bestehender Operatoren oder syntaktischer Elemente zu erzeugen.

Macros

Während neue Operatoren die Syntax nur ein Stückchen verändern, kann mit Macros deutlich stärker eingreifen (und, wenn man es falsch macht, auch mehr kaputt machen).

Im einfachsten Fall sind Macros Textersetzungen, die während der syntaktischen Analyse des Quellcodes durchgeführt werden.

Wer zum Beispiel die interpolierende Form von qx/.../ (Ausführen eines externen Kommandos und zurückgeben der Ausgabe) von Perl 5 vermisst, und zu faul ist qq:x/.../ stattdessen zu schreiben, kann sich zwei Buchstaben sparen, indem er ein Macro dafür definiert:

macro qx { 'qq:x ' }
my $result = qx/ls $dir|grep pl\$/;

Das führt erst eine Textersetzung durch, und parst dann das Ergebnis dieser Textersetzung als ganz normalen Perl 6 Quellcode.

is parsed

Macros können die is parsed-Eigenschaft haben, die angibt, welche Regex oder Funktion den Text erkennt, der von einem Macro gematched wird.

Auf diese Art kann man zum Beispiel HTML-Kommentare in Perl 6 verfügbar machen:

macro circumfix:« <!-- --> » is parsed rx/ .*? / { '' }

say 2 + 3; <!-- Ergebnis: 5 -->

Hier wird ein Circumfix-Operator definiert, der mit <!-- beginnt und mit --> endet. Alles dazwischen wird von der regex .*? geparst, d.h. dort wird kein Perl 6-Code erwartet, sondern nur noch das, was diese Regex matcht.

Das Ergebnis ist in diesem Fall der leere String, da Kommentare keine Funktion im Programmcode übernehmen.