V
5  
Tags
Attachments
Perl 6
Perl 6 Tutorial Part 8


Herzlich willkommen auch zum achten und letzten Teil dieses tiefsch�rfenden
Tutoriums f�r werdende Perl 6-Programmierer. Diesmal wird es sogar noch eine Ebene
tiefer gehen, zu den Interna der Sprache und wie sie befragt und ver�ndert werden.
Da diese Bereiche st�rker theorielastig sind und von Pugs und Rakudo bisher kaum
implementiert werden, besteht diese Folge aus mehr Text und weniger Beispielen
als �blich.

Wozu Metaprogrammierung?

Von Anfang an ging es bei diesem Projekt nicht nur darum vergangene Irrt�mer
auszub�geln und den aktuellen Stand dynamischer Sprachen in vielem wieder ein- und
�berholen. Perl 6 sollte auch nach l�ngerer Reifung ebenfalls lange halten. Was
bedeutet, da� die Sprache f�hig sein mu�, k�nftige Anpassungen zu unterst�tzen,
ohne das die Implementation des Interpreters angefasst werden wird. Einfacher
gesagt: Perl 6 sollte bessere M�glichkeiten der Metaprogrammierung erhalten,
als die derzeit ber�chtigten I<Sourcefilter>, die abgeschafft wurden, da sie viel
zu langsam und fehleranf�llig sind, um in echtem Produktionscode eingesetzt zu werden.

Das ganze hat aber noch eine anderen Grund. Viele der weitverbreiteten Sprachen
haben etliche Varianten oder auch Ableger, da verschiedene Fachbereiche oder
Anwendergruppen verschiedene Betrachtungsweisen kennen oder bevorzugen.
Um die Aufsplitterung von Perl in Derivate wie es z.B. mit PHP geschah zu verhindern,
mu� Perl noch wandlungsf�higer werden.
Und nur eine gemeinsame Basis macht eine Infrastruktur und damit alle Beteiligten
wirklich m�chtig (siehe CPAN).
Gerade in den letzten Jahren wird das mit dem Modewort I<domain specific language>
(DSL) zusammengefasst, wenn Sprachen einer Aufgabenstellung angepasst werden k�nnen,
um diese effektiv und f�r die mit der Materie Vertrauten verst�ndlich zu l�sen.
Flexibilit�t (TIMTOWTDI) hatte sich Perl schon immer auf die Fahnen geschrieben
und dies wird mit Perl 6 nur noch etwas weiter getrieben. Perl 6 ist in Wirklichkeit
eine Metaprogrammiersprache, die sich zur Laufzeit in fast alles Denkbare verwandeln
kann. Dies ist in Ordnung so, mein Larry Wall, da alles erlaubt sein soll,
wenn man es vorher explizit deklariert. Und Lernende werden mit einem ausgewogenem
Satz vordefinierter Befehle zu einem "guten Stil" angeleitet, bis sie f�hig werden,
alle Regeln zu ver�ndern. Und je mehr sie lernen diese Regeln zu ihrem Vorteil
zu �ndern, umso k�rzer, effektiver und oft auch lesbarer werden ihre Programme.

Dieser Ansatz bringt nicht nur Freiheit f�r den Programmierer sondern erlaubt die
immer komplexer werdenden Softwaresysteme kompakter und beherrschbarer zu schreiben.
Ein Blick in die Java-Welt zeigt die Nachteile hierarchisch-logischer Strukturen,
die oft zu unbeweglich und �berladen und damit schwer lesbar und ver�nderbar sind.
Aber selbst in der Welt von Perl und Ruby lernte man in den letzten Jahren, da�
die zuerst gepriesenen Web-Frameworks schnell sehr umfangreich werden. Jedes ist
eine Welt f�r sich, die erlernt sein will, obwohl sich viel Funktionalit�t �hnelt.
Deshalb entstanden in letzer Zeit mit Rack, Mojo und anderen Unterfangen Software,
die sich auf einzelne Probleme konzentriert, um diese in logischen Einheiten mit
eine m�glichst einfachen API handhabbar zu halten. Die Idee dahinter ist nicht neu,
denn Perl 1.0 brachte Shellbefehle in die C-Syntax brachte, um komplexere Probleme
kurz und und dem menschlichen Denken nah zu l�sen. Sp�ter erf�llten in Perl 5.n
gute Module den gleichen Zweck, nur das sie bereits von Programmierern der Sprache
zugef�gt werden konnten, ohne den C-Quellcode von Perl anzufassen. Das neue Perl 6
ist eine logische Fortsetzung dieser Entwicklung zu einer Sprache die quasi "fl�ssig" ist,
und in verschiedene Situationen die Syntax annehmen kann, der dem Ideal des Benutzers
entspricht. Dadurch fallen Barrieren, die bisher durch Programmiersprachgrenzen
aufgestellt wurden. Gro�e Systeme bestehen zunehmend aus Teilen die in verschiedenen
Sprachen und Versionen geschrieben wurden, die sich nur in Details unterscheiden
k�nnen, aber dennoch von zus�tzlicher I<Middleware> "zusammengehalten" werden m��en.
Diese Teilsystem werden oft kaum mehr angefasst, da sie ausgereift, zuverl��ig
aber nicht immer leicht wartbar und auch nicht leicht erneuerbar sind. Deshalb
wachsen diese Flickenteppiche zu ungeahnten Komplexit�ten und es gibt bereits
Lehrst�hle f�r Professoren, die Menschen beibringen wollen mit solchen Unget�men
umzugehen. Besser w�re es doch, wenn solche Software gleich in Perl 6, vielleicht
auch Ruby oder Lisp geplant wird, um diese Komplexit�t nicht entstehen zu lassen.
Und selbst wenn "unber�hrbare" Software Teil einer Anwendung ist, kann eine sehr
anpassungsf�hige Sprache Kommunikation mit wenig Programmieraufwand erm�glichen,
und Perl bleibt was es immer war: eine I<glue-language>.

Wo beginnt Metaprogrammierung ?

Wenn eine kleine I<sub> den verf�gbaren Wortschatz erweitert, ist das bereits
Metaprogrammierung? Das kommt auf den Fall an. Subroutinen k�nnen dazu verwandt
werden etwas Ordnung in Spaghetticode zu bringen, was unter das Schlagwort
"strukturierte Programmierung" f�llt. Da die Errungenschaften von C<if>, C<while>
und Routinen seit den 70er-Jahren Allgemeingut wurden, f�llt der Begriff heute selten.
Wesentlich h�ufiger h�rt man dieser Tage dagegen das Wort "funktionale Programmierung".
So hei� ein Programmierstil der sehr abstrakt ist und daher in die Metaprogrammierung
hineinragt. Mark Jason Dominus beschreibt in "Higher Order Perl" genauer wie das
in Perl aussieht (Buchrezension in $foo Winter/2008). Das Buch ist wohl zum Teil
nach den "Higher Order Functions" benannt. Das sind Routinen die eine Aufgabe
sehr abstrakt l�sen und deshalb sehr vielseitig einsetzbar sind. Viele Perlianer
verwenden bestimmt solche "Higher Order Functions", ohne den Begriff zu kennen.
Z.B. C<map> und C<grep> fallen in diese Kategorie. Aber mit Perl 5.0 konnte man
auch bereits selber solche Functionen schreiben, da schachtelbare Namensr�ume f�r
Variablen und Codereferenzen als Parameter anwendbar waren. �hnlich der OOP war
in Perl 5 fast alles m�glich, nur vieles jetzt einfacher. Eine wichtige Technik
in der funktionalen Programmierung ist z.B. das I<Currying>. Auf deutsch: das
Erstellen einer Codereferenz auf eine Funtion h�herer Ordnung (einen Alias), bei
der bestimmte Parameter festgelegte Werte haben und nur die restlichen Parameter
bestimmt werden k�nnen. Also wenn eine Funktion C<potenz> beliebige Potenzen
berechnet(ja es ginge auch mit C<**>, aber folgender Code entspricht einem
funktionalem L�sungsansatz):

subset Num+ of Num where { $_ > 0 };
subset Num- of Num where { $_ < 0 };
subset Zero of Num where { $_ == 0 };

multi sub potenz (Num :$basis!, Zero :$exponent!) {
return 1;
}

multi sub potenz (Num :$basis!, Num- :$exponent!) {
return 1 / potenz( $basis, -$exponent);
}

multi sub potenz (Num :$basis!, Num+ :$exponent!) {
return $exponent * potenz( $basis, $exponent - 1 );
}

Ein Funktion C<quadrier> w�rde ich nun explizit wie folgt erhalten:

&quadrier := &potenz.assuming( exponent => 2 );
# alternative Schreibweise:
&quadrier := &potenz.assuming( :exponent(2) );

Die l��t sich wie erwartet aufrufen:

say quadrier(5); # ergibt 25

C<.assuming> (englisch f�r angenommen) dr�ckt in einer Alltagssprache genau aus
worum es hier geht: C<quadrier> entspricht C<potenz>, unter der Annahme, da� der
Exponent 2 ist. Das C<:=> ist keine Zuweisung sondern ein I<binding>. P6 kennt
weder direkten Manipulation der Symboltabelle mit Typeglobs noch Referenzen (der
Schr�gstrich (I<backslash>) erzeugt jetzt I<captures>, siehe Teil 6). Um einen
Alias auf den Inhalt einer Variable zu erhalten, bindet man diese Variable mit
C<:=> an eine Andere.

my $planeten = 7;
my $planety := $planeten;
$planeten = 9;
say $planety; # gibt 9
say "yes" if $planety =:= $planeten;

Das letzte Beispiel f�hrt die Ausgabe aus, da die Variablen an das gleiche Objekt
gebunden sind. Ein Binden zur Kompilierungszeit mit der Schreibweise C<::=> mag
selten gebraucht werden, aber das Ausf�hren von Routinen zum fr�hstm�glichen
Zeitpunkt wesentlich �fter. In Perl 5 schrieb man dazu den Code in einen BEGIN-Block,
was weiterhin m�glich ist, aber Perl 6 kennt noch ein anderes Konzept, das dem
�hnelt, aber weitaus m�chtiger ist. Manche behaupten sogar, da� vor allem diese
Macros LISP m�chtiger machen als alle anderen Sprachen, was in den 60er und 70er
Jahren auch gestimmt haben mag.

Die wunderbare Welt der Macros

Macros sind (wie angedeutet) Routinen, die beim Kompilieren ausgef�hrt werden.
Im Gegensatz zu einer C<sub>, �ndern sie die Sprache, da sie statt eines Wertes,
einen AST (Baumstruktur, die als Zwischenform beim Kompilieren entsteht) liefern,
der beim Kompilieren anstelle jedes Macroaufrufes in den AST des Programmes
eingef�gt wird, bevor die Ausf�hrung beginnt.

In C ist das bei weitem einfacher. Hier sind Macros lediglich Textbausteine die
an die mit dem Makronamen markierten Stellen im Quellcode eingef�gt werden, bevor
der Kompiler sein Werk beginnt. Die alten Perl 5-Sourcefilter taten ihr Unwesen
nach dem gleichen Prinzip, nur da� hier die volle Kraft der P5-Regex am Werke war.
Das kann subtile, schwer zu entdeckende Fehler erzeugen, da jeder Macroprozessor
blind f�r die Bedeutung der Sprachsyntax ist und der von ihm erstellte Quelltext
nirgends einsehbar ist. Perl 6-Macros haben standardm��ig (wie alle Bl�cke) keine
Seiteneffekte auf die lexikalische Struktur der umgebenden Quellen.
So etwas nennt die Fachwelt: hygienische Macros. Folgende Makros sind hygienisch:

macro summe { 3 + 4 }
macro summe { '3 + 4' }
macro summe is parsed { 3 + 4 }

C<parsed> ist ein Trait von Routinen da� nur Macros wirklich ben�tigen, aber da
es default ist, kann es auch weggelassen werden. Und auch die Verwendung der
Macros ist denkbar einfach.

say 2 * summe; # 14
say 2 * summe(); # 14
say 2 * &summe(); # 14

Auch wenn alle Aufrufe C<14> ergeben, so sind sie nicht identisch, da der dritte
erst zur Laufzeit aufgel�st wird. Doch manchmal sind dreckige Macros genau was
man m�chte und dann schreibt man.

macro summe is reparsed { 3 + 4 }

Wird dieses Macro im vorigen Beispiel aufgel�st, hie�e das Ergebnis C<10>.
Denn dieses Macro gibt nur einen String zur�ck, der zusammen mit dem umgebenden
Quellcode compiliert wird. Dabei gilt die alte Regel: Punkt- vor Strichrechnung.
Da diese Funktionsweise derartige Probleme provoziert, hat sie die beschriebene
Kindersicherung und es wird standardm��ig von Macros ein AST zur�ckgegeben.
Aber auch au�erhalb von Macros kann dies getan werden. I<quoting> mit dem Adverb
C<:code> (siehe letzte Folge) und ein C<quasi> vor geschweiften Klammern oder
Anf�hrungszeichen kann das (I<quasiquoting>) bewirken.

return quasi { say "foo" };
return Q :code / say "foo" /;

Beide Zeilen liefern keine Referenz auf eine Routine sondern ein kompiliertes
St�ck Programm. Folglich ist ein Macro eine zu C<BEGIN> ausgef�hrte Routine
deren R�ckgabewert derart C<quasi> kommentiert (I<gequoted>) ist. Dies ist eine
sehr elegante Eigenschaft von Perl 6, da� jedes Sprachelement mit den Bordmitteln
der Sprache beschrieben werden kann. Deswegen kann die Syntax, so reichhaltig sie
auch scheinen mag, auf einen wesentlich kleineren Kern reduziert werden, aus dem
sich beliebige Sprachen aufbauen lassen. Deshalb gilt f�r Perl 6 was John Foderaro
einst �ber Lisp sagte: "es ist eine programmierbare Programmiersprache".
Weil Perl syntaktisch viel reichhaltiger als Lisp ist, da� nur Funktionsnamen,
Wertelisten und runde Klammern kennt, m��en Perl-Macros wesentlich mehr k�nnen,
um wirklich alle Sprachbestandteile erweitern zu k�nnen. Einen neuer Operator
(z.B. f�r die mathematische Fakult�t-Funktion) wird so erzeugt:

macro postfix:<!> { * 1..$^n }
macro postfix:('!') { * 1..$^n }

Das gew�hlte Operatorsymbol kann auch in andere Klammern als den Spitzen geh�llt
sein, aber der Vorsatz "postfix:" ist entscheidend, da wir einen Postfix-Operator,
also einen nachgestellten Operator (wie in C<$p++>) definieren wollen. Um noch zu
bestimmen wo der neue Operator seinen Platz in der Vorrangtabelle hat, k�nnte man
hinzuf�gen:

macro postfix:<!> is equiv(&postfix:<++>) { ... }

Das k�nnte man auch mit C<is tighter> oder C<is looser> definieren. Dann bek�me
der Operator eine eigene Spalte in der Vorrangtabelle die jeweils �ber oder unter
dem angegebenen Operator liegt.

Diesem Schema entsprechend gibt es eine lange Reihe von Schl�sselw�rter die jede
Art von Operator oder Schl�sselwort bezeichnen. Es lassen sich eigne Arten des
I<Quoting>, Regex-Befehle, Spezialvariablen oder neue sekund�re Sigils einf�hren.
Wenn jemand XML-Kommentare in seinem Perl haben m�chte so reicht ein:

macro circumfix:�<!-- -->� ($text) is parsed / .*? / { "" }

C<$text> ist der Parameter, der den Text zwischen den Kommentarzeichen beinhaltet.
Nach dem C<is parsed> steht die Regex mit der geparsed werden soll. In diesem
Fall hat das <reparsed> den Vorteil, da� die Regex mit Regeln formuliert werden
kann die erst sp�ter definiert werden.

Da entlang Alice

Aber die Manipulation der Sprache kennt noch eine Ebene. Es ist sogar m�glich
die Regeln zu �ndern mit denen der Interpreter den Quellcode einliest. Seine
Arbeitsweise wird in der STD.pm mit der Hilfe von P6-Regex-Grammatiken definiert.
Wie diese formuliert werden und aufgebaut sind, behandelte die letzte Folge.

Intern wird die Sprache in mehrere Teilsprachen ("slangs") gegliedert.
Die Kernsprache ist z.B. eine Grammatikobjekt, auf das mit C<$~MAIN> zugegriffen
werden kann. Die Regeln f�r das Kommentieren stehen unter C<$~Q>.
Und die Regex die bestimmen wie Regex einzulesen sind, finden sich unter C<$~Regex>.
C<~> ist die Twigil dieser Sondervariablen. Und da Grammatiken nach au�en normale
Objekte sind, k�nnen sie abgeleitet und ver�ndert werden wie jedes andere Objekt
auch. S�mtliche �nderungen gelten nur f�r den aktuellen Namensraum (I<scope>)
und die I<defaults> k�nnen jederzeit wiederhergestellt werden, da sie unter
C<%?LANG> gespeichert sind.

Zeig mir dein Inneres!

Objekte k�nnen in Perl 6 sehr stark durchleuchtet und ver�ndert werden. Jede
Elternklasse, jede Methode und jede Signatur (mit C<$obj.methode.signature>) kann
abgefragt oder verschiedenst gepr�ft werden. C<$obj.WHERE> nennt die Speicheradresse,
C<.WHAT> den Typ, was in etwa dem Befehl C<ref> in Perl 5 entspricht.

Es lassen sich mit Roles beliebige Methoden zu Laufzeit in ein Objekt einf�gen,
aber der direkteste Weg daf�r ist wohl:

augment slang Regex {
token regex_metachar:<^> { ... }
}

C<augment> (englisch f�r einblenden) f�gt nur Regeln (Methoden) in die Grammatik
(Klasse) ein, soll eine gesamte Slangdefinition ausgetauscht werden, dann ist
C<supersede> (englisch f�r �berlagern) das Mittel der Wahl.

Es gibt noch viele weitere Sondervariablen, wie f�r den umgebenden Block
(C<&?BLOCK>) oder die umgebende Routine (C<&?ROUTINE>) mit denen sich weit mehr
tun l��t als noch in Perl 5, z.B. lassen sich alle Sprungmarken im aktuellen Block
mit C<&?BLOCK.labels> auflisten. Auch C<&?ROUTINE.name> ist praktisch, wenn man
nicht merh wei� wo sich die Ausf�hrung gerade befindet. Doch dies sieht man alles
in den Tabellen im Anhang B meines Wiki-Kompendiums, siehe Artikelende.

Was sich sonst noch tat

Zu Beginn dieses Tutorials k�ndigte ich an, mit jedem Teil auch alle �nderungen
bereits erw�hnter Syntax zu dokumentieren. Doch zum Gl�ck waren es weit weniger,
als angenommen, soda� eine kleine Aktualisierung am Ende des letzten Teils gen�gt.
Gleich im ersten Teil wies ich auf eine potentielle Stolperfalle:

$b = =$a; # Zuweisung einer Zeile aus einem Datenstrom
$b == $a; # numerischer Vergleich

Diese ist mittlerweile behoben, denn der prefixe Operator C<=> wurde ersatzlos
gestrichen. Statt dessen sollte man die Methoden C<.lines> und C<.get> verwenden.
C<.lines> liefert einen Iterator und C<.get> tats�chlich Textzeilen.

my $name = "artikel.txt";
my $handle = $name.open err die "Kann '$name' nicht �ffnen: $!";
my $ganzer_inhalt = $handle.slurp;

for $handle.lines > $zeile { ... }
while $handle.get
> $zeile { ... }
$zeile = $handle.get;
$handle.close;

Da der Kopf einer C<while>-Schleife im Skalarkontext und C<eager> evaluiert wird,
w�rde C<.lines> dort einen Array mit allen Zeilen liefern. Da jedoch C<for> den
Arraykontext forciert der standartm��ig C<lazy> ist, wird dort bei jeder Iteration
jeweils eine Zeile �berwiesen. Im Skalarkontext w�rden alle Zeilen mit C<~>
verbunden geliefert.

Der neue Metaoperator C<R> kam hinzu. Er vertauscht lediglich die Operanden,
R steht f�r I<reverse> (englisch r�ckw�rts).

say 3 R- 4; # sagt 1
say 2 R** 3; # sagt 9

Der Kreuz-Metaoperator wurde vereinfacht. Von nun an reicht es dem Operator ein
C<X> voranzustellen, vorher hie� das noch z.B. C<X~X>.

<a b> X~ <1 2> # <a1 a2 b1 b2>

Der Hyper-Metaoperator l��t sich aber nicht so vereinfachen, da die Pfeile
(<< und >>) bestimmen, wie der Operator auf Dimensionalit�t der jeweiligen Seite
reagieren soll.

Neben autogenerierten positionalen Parametern (erkennbar an der twigil C<^>), gibt
es nun autogenerierte benannte Parameter, deren Twigil C<:> sich nahtlos in die
Syntax zur Deklarierung benannter Parameter einf�gt.

Selbstverst�ndlich gab es noch weit mehr �nderungen, doch diese sind oft subtiler
oder liegen au�erhalb der behandelten Themen. Einen tieferen Einblick gew�hrt das
Tutorial in der Wiki der deutschen Perl-Community unter
L<wiki.perl-community.de/cgi-bin/foswiki/view/Wissensbasis/PerlTafel>,
oder einfacher zu tippen: L<de.perl.org> und dann auf Wiki > Wissensbasis > Tutorials
klicken. Viele hilfreiche Texte beinhaltet auch die Seiten von Moritz Lentz unter
L<perl-6.de>. Dieses Tutorial wurde jetzt in eine Wiki online gestellt unter:
Lhttp://wiki.perl-community.de/cgi-bin/foswiki/view/Wissensbasis/Perl6Tutorial
und kann nun von jedem kommentiert und verbessert werden. Ich habe vor es weiter
zu verbessern und auch ins Englisch zu �bersetzen, wo es in der Wiki der TPF
unter L[perl tablets] zu finden sein wird.


Previous Chapter | Overview


 

Upload Files

Click "Browse" to find the file you want to upload. When you click "Upload file" your file will be uploaded and added to the list of attachments for this page.

Maximum file size: 50MB

 
 
 
File Name Author Date Uploaded Size

Save Page As

Enter a meaningful and distinctive title for your page.

Page Title:

Tip: You'll be able to find this page later by using the title you choose.

Page Already Exists

There is already a page named XXX. Would you like to:

Save with a different name:

Save the page with the name "XXX"

Append your text to the bottom of the existing page named: "XXX"

Upload Files

Click "Browse" to find the file you want to upload. When you click "Add file" this file will be added to the list of attachments for this page, and uploaded when you save the page.

 
 
 
Add Tags

Enter a tag and click "Add tag". The tag will be saved when you save the page.

Tag: 

Suggestions: