Syntaktische Neuheiten in Perl 5.20

Während des letzten Jahres bekam die Skriptsprache Perl Syntaxerweiterungen, die klarere Programme verheißen. Dieser Artikel prüft das mit der großen Lupe und geht der Frage nach, ob da noch mehr folgt.

In Pocket speichern vorlesen Druckansicht 67 Kommentare lesen
Lesezeit: 14 Min.
Von
  • Herbert Breunung
Inhaltsverzeichnis

Während des letzten Jahres bekam die Skriptsprache Perl Syntaxerweiterungen, die klarere Programme verheißen. Dieser Artikel prüft das mit der großen Lupe und geht der Frage nach, ob da noch mehr folgt.

Als 2012 auf heise Developer die Orakelmaschine unter Dampf stand, lag sie mit Blick auf die Mai 2014 freigegebene neue Perl-Version 5.20 gar nicht so verkehrt. Zwar ist das Perl-eigene MOP() noch in Arbeit, und damit sind echt gekapselte Objekte mit deklarativer Syntax im Kern immer noch nicht angekommen (man hat ja derweil Moo[se]). Aber dafür kamen Signaturen, die – ebenfalls durch Perl 6 angeregt – mit Moose weite Verwendung fanden, bevor die Sprachentwickler sie konsenstauglich und im Einklang mit dem Rest der Sprache einführen konnten. Die sogenannten Porter haben aus der übereilten Aufnahme des Smartmatch-Operators (~~) in Perl 5.10 gelernt. Der wird übrigens trotz anderslautender Vermutungen nicht abgeschafft, sondern wahrscheinlich nur vereinfacht.

Was für Verwirrung sorgte, war die nachträgliche Abstufung des Operators auf den Status "experimental" – bemerkbar durch die gleichnamige Klasse von Warnungen, die erstmals Perl 5.18 ausgab. Zwar waren die Entwickler stets der Meinung, dass der Smartmatch-Operator noch nicht ausgereift sei und spätere Änderungen nach sich ziehen würde. Doch eine Bemerkung dazu in der Dokumentation war kein ausreichendes Kommunikationsmittel. Deshalb bringt Perl 5.20 zusätzlich das Pragma experimental, das alle experimentellen Fähigkeiten kennt. Überdies hilft es mit einem:

use experimental 'postderef';

folgende zwei Zeilen abzukürzen:

use feature 'postderef';                 # Funktionalität aktivieren
no warnings "experimental::postderef"; # Warnungen unterdrücken

So bestätigt der Nutzer, dass er keinen Anspruch auf eine unveränderte Verfügbarkeit der Funktion besitzt. Diese darf dann im aktuellen Block, aktuellen Paket, bis zum Dateiende oder bis zum nächsten

no experimental 'postderef';

verwendet werden (je nachdem was eher einsetzt). Experten würden sagen: experimental ist ein lexikalisches Pragma. use feature qw...;, ebenfalls lexikalisch, dient eher dazu, neuere, aber etablierte Features einzeln zuzuschalten, um Inkompatibilitäten zu vermeiden. Wer aktuell jedoch nicht experimentell sein mag, der verkürzt zum Beispiel auf use v5.20; . Eine Quelle darüber, was wann kam oder ging, selbst wenn es keinen Namen erhielt, ist das Modul Syntax::Construct.

Die in der Dokumentation als "Key/Value Hash Slices" und "Index/Value Array Slices" beschriebene Fähigkeit nutzt Perl 5.20 auch ohne Zustimmung eines Pragmas. Denn Pumpking Ricardo Signes (Kopf der Porter) sieht sie mehr als Fehlerbehebung als eine Erweiterung an. Um das zu verstehen, hilft ein Rückblick ins Jahr 1987, als Perl noch in den Windeln lag und an Klassenhierarchien oder verschachtelte Datenstrukturen nicht zu denken war. Der werdende "Vater" Larry Wall hatte Variablen mit Vorzeichen (Sigil) versehen, um die Sprache später leichter zu erweitern. Damit sollten sich auch Variablen innerhalb doppelter Anführungszeichen und Heredocs einfacher erkennen und interpolieren lassen.

Wie bekannt haben Skalare die Sigil '$', Arrays '@' und Hashes (assoziative Arrays oder auch Dictionaries) '%'. Diese Zuordnung ist jedoch nicht fest, da der Linguist Wall ihnen eine grammatikalische Bedeutung zuschreibt: '$': Einzahl, "@": Mehrzahl, '%': paarweise Zuordnung. Folgerichtig benutzt man '$', wenn ein einzelner Wert einer Liste gefragt ist und so weiter: (Der Klarheit dieser Beispiele wegen wurde $OUTPUT_FIELD_SEPARATOR = ' '; gesetzt.)

my @array = ('d' .. 'f');
say @array; # alle Werte eines Array : d e f
say @array[0,1]; # Teilarray mit zwei Werten : d e
say $array[2]; # ein Wert aus gleichen Var.: f

my %hash = (a => 1, b => 2, c => 3 );
say %hash; # alle Schlüssel und Werte : a 1 b 2 c 3
say @hash{'a','b'}; # Werten zweier Schlüssel : 1 2
say $hash{'c'}; # mit Schlüssel verknüpfter Wert: 3

Aus welchem Containertyp entnommen wird, sieht man also leichter an den Klammern – Array: '[]' und Hash: '{}'.

27 Jahre später entdeckte die Perl-5-Mannschaft (beim Lesen der Perl-6-Spezifikation), dass sich Variablen nach der gleichen Logik noch weiter konjugieren lassen:

say %array[0,1];    # abwechselnd Index/Wert    : 0 d 1 e
say %hash{'a','b'}; # abwechselnd Schlüssel/Wert: a 1 b 2

Genauer gesagt wurde die Schreibweise entdeckt. Die soeben gezeigten Möglichkeiten hat die nächste Generation Perl bereits jahrelang in Gebrauch. Sie greift allerdings dafür auf das Adverb :p (wie pair) zurück, denn für Perl 6 verwarf Larry Wall die Idee der änderbaren Sigil.

say @array[0,1] :p; # Ausgabe: 0 => "d" 1 => "e"
say %hash<a b> :p; # Ausgabe: "a" => 1 "b" => 2

Bei Referenzen lässt sich in Perl 5 der Zauber mit einem Zeichen mehr vollführen:

my $arrayref = ['d' .. 'f'];
my $hashref = {a => 1, b => 2, c => 3 };
say %$arrayref[0,1]; # Index/Wert - Liste : 0 d 1 e
say %$hashref{'a','b'}; # Schlüssel/Wert - L.: a 1 b 2

Pair Slices sind nützlich, um Teile eines Hashes oder Arrays (mit Schlüsseln) in einen anderen Hash zu überweisen, ohne die Daten in einer for-Schleife oder mit map einzeln umzuschaufeln. Für die häufiger gestellte Aufgabe "über Schlüssel-Wert-Paare iterieren" ist es aber nur bedingt brauchbar, da sich keine Reihen- folge der Paare garantieren lässt. Hashes vermögen das aus Sicherheitsgründen nicht. Die Pair Slices sind jedoch in einem Hash zwischenzuspeichern, weil each nur damit (oder einem Array) funktioniert. Wen das nicht stört, der arbeite hiermit:

my %teilhash = %hash{'a','b'};
while (my($key, $value) = each %teilhash){
say "$key => $value";
}

Perl 6 hat den großen Vorteil, einen eigenen Datentyp für Schlüssel-Wert-Paare zu besitzen. Damit iteriert man reibungslos über Paare in jeder gewünschten Reihenfolge:

for %hash<a b> :p -> $pair {say $pair}     # fast gleiche Ausgabe wie:
for %hash<a b> :p -> $pair {say $pair.key, " => ", $pair.value}

Änderbare Sigils besitzen selbstredend auch Nachteile. 1994, mit der Ankunft der Referenzen, erwuchsen aus ihnen sogar echte Probleme. Nun konnte eine Array-Element wiederum einen Array enthalten. $array[2] gibt aber nur eine Referenz (Typ und Adresse) zurück, beispielsweise ARRAY(0xfc64c8). Den Inhalt entlockt die passende Dereferenzierung: @{ $array[2] }. ("Gib mir die Inhalte, auf die eine Referenz verweist!") Geübte Perl-Programmierer kommen auch mit kniffligeren Fällen wie

@{ $array[$arrayindex][5]{ $hashkey->[4] }[2] }

zurecht. Nur braucht es eben für einige hin und her wandernde Blicke Zeit, um die passenden Klammern zuzuordnen.

$array[2]->@*  # gesamter Unterarrayinhalt
$array[2]->$#* # Anzahl der Elemente

Solche Ausdrücke umfassen ein Paar Klammern weniger und lassen sich bequem von links nach rechts lesen. Darüber hinaus enthalten sie fast keine neue Syntax. -> bedeutet seit Perl 5: "Tu was mit der Referenz links des Pfeils" und ebenso steht @ und $# für das Gleiche wie bisher. Einzig neu ist der Stern – in Perl 6 der "Whatever"-Operator, der oft für "Gib mir alles" steht.

Wenn nicht alle Werte, sondern nur ein Ausschnitt (slice) benötigt wird:

$array[2][7];          # ein Wert, wie bekannt
$array[2]->[7]; # dasselbe
$array[2]->@[7]; # kein Vorteil durch neue Schreibweise soweit
$array[2]->@[5,6]; # 2 Werte, gleichbedeutend mit:
@{$array[2]}[5,6]; # ging bisher nur so
$array[2]->%[5,6]; # auch Pair Slices möglich
$hash{'a'}->%{'g','e'};# bei verschachtelten Hashes entsprechend so

Zusätzlich lassen sich derart Skalar-Referenzen ($), Code-Referenzen (&) und sogar Typeglobs (*) (für direkte Zugriffe auf die Symboltabelle) postfix dereferenzieren, was selbst innerhalb doppelter Anführungszeichen erkannt und evaluiert wird. Besonders Typeglobs sind lediglich für sehr spezielle Einsätze gedacht. Aber ein neues Feature, das alle Möglichkeiten der bisherigen Sprache abdeckt und keine neuen Besonderheiten einführt, zeugt von gutem Sprachdesign.

Das trifft nicht auf das mit Perl 5.14 eingeführte Feature autoderef zu, was den Befehlen push, pop, shift, unshift, splice, keys, values und each erlaubt, selbsttätig zu dereferenzieren. Auch um Perls Verhalten berechenbarer zu gestalten, ist sein geordneter Abgang beschlossen.

Den größten Beifall, aber auch die bissigsten Kommentare erhielt eine Fähigkeit, deren Ankunft ein wenig wie eine Meldung klingt, 2014 habe ein widerspenstiges Bergdorf beschlossen, die Elektrizität einzuführen. Es handelt sich um die in runde Klammern gesetzte Liste der Parameter, die von einer Subroutine/Funktion erwartet werden. Immerhin ist die Gestaltung in Perl 5 durchdacht und enthält etwas mehr, als man erwarten könnte. Dass die "signatures" wie die "postderef" als "experimental" eingeweiht wurden, war reine Vorsicht. Sollten keine schweren Fehler erkennbar werden, wird der Status frühstmöglich aufgehoben, es sei denn, Peter Martini vermag es, getreu seiner Pläne, benannte Parameter und eine Art minimale Typisierung hinzuzufügen. Bis jetzt sind alle Parameter positional und die neuen Signaturen eher ein Weg, @_ nicht anfassen zu müssen – selbst wenn diese Spezialvariable unverändert alle übergebenen Argumente enthält. Ein kleines sinnfreies Beispiel:

use experimental 'signatures';

sub sag ($subjekt, $praedikat, $objekt) {
say "$subjekt $praedikat $objekt.";
}

Neben dem Offensichtlichen tut es zwei Dinge:

  1. Enthielt der Aufruf der Routine sag mehr oder weniger als drei Argumente, so gibt es einen Fehler, wie ihn auch die erzeugt.
  2. Drei Variablen werden lexikalisch lokal (wie mit my) angelegt und mit den Argumenten gefüttert.

Weist man selber in der Signatur einem Parameter einen Default-Wert zu, was ein beliebiger, evaluierbarer Ausdruck sein darf, so deklariert man ihn gleichzeitig zu einem optionalen Parameter.

sub sag ($subjekt, $praedikat, $objekt = held_des_tages()) {

Nun sind auch zwei Argumente akzeptabel. Variadische Funktionen gestatten jedoch unbegrenzt viele Argumente, die ab einer Stelle optional sind.

sub netz_ausgabe ($server, $datei, @zeilen) {

Jetzt müssen es mindestens zwei Argumente sein, nach oben offen. Im Perl 6-Slang heißt der letzte Parameter "slurpy array", weil er quasi alle restlichen Argumente aufschlürft. Dabei ist es Perl egal, ob die Werte beim Aufruf aus Skalaren, Arrays oder Hashes kamen, solange nicht explizit Referenzen überreicht wurden. Eine besondere Form wäre:

sub diff_anwenden ($datei, %zeilen) {

Denn hierbei wird darauf geachtet, dass %zeilen eine gerade Anzahl an Werten bekommt: Paare aus Zeilennummer und Inhalt.

Es gibt aber auch Fälle, in denen Daten nur übergeben werden, um Schnittstellen zu erfüllen. Der Empfänger könnte sie vor Erhalt wegwerfen, um Speicher zu sparen.

sub sag ($, $praedikat, $objekt, @) {

Die neue Syntax ersetzt natürlich keine Prototypen. Sie wurden erfunden, um die eigenen Routinen mit dem speziellen Verhalten an Parameterauswertung auszustatten, über das sonst nur Kernbefehle verfügen. Heutzutage steckt da mehr Magie drin, als die meisten wissen möchten. Dummerweise setzt man auch Prototypen in runde Klammern, weshalb ihnen bei aktivierten Signaturen ein erklärendes Attribut voranzustellen ist:

sub sag :prototype($$;$) ($subjekt,...) {

Der einfachen Nutzbarkeit wegen gab man den Signaturen nicht allzu viel Intelligenz mit, was die Unannehmlichkeit birgt, bei Methoden das notorische ($self,...) als ersten Parameter selbst einfügen zu müssen oder respektive new ($class,...). Mit Method::Signatures oder der Moose-Erweiterung MooseX::Method::Signatures wäre das nicht nötig – ein direkter Vergleich aber auch reichlich unfair. Diese Module können wesentlich mehr und kopieren beinahe buchstabengetreu die Perl-6-Syntax und -Semantik. Eine Kernfunktion, die nicht mit dem sehr ähnlichen Modul signatures zu verwechseln ist, hat einfach, klar, schnell und leichtgewichtig zu sein.

Über ein Dutzend Module wetteiferten seit Jahren im CPAN um das optimale Maß an Funktion und Schreibaufwand. Signaturen wurden im Gefolge der Moose-OOP ein großes Thema für die Perl-Gemeinde, zu dem sich endlich durch die unermüdliche Agitation und Vorarbeit von Peter Martini und die Implementierung von Andrew "Zefram" Main ein tragfähiger Konsens bildet. Bei der Typisierung zeichnet sich eine ähnliche Entwicklung ab, jedoch weniger druckvoll und noch nicht so weit fortgeschritten.

Die "pair slices" ermöglichte Ruslan Zakirov und 'postderef' war die Arbeit von Father Chrysostomos (Ordensname). Im Bereich der freien Software hängt vieles von der schwer berechenbaren Initiative Einzelner ab. Und dennoch erkannte eine Entwicklerversion von Perl 5.21.x den Unicode-7-Standard bereits zwölf Stunden nach seiner Freigabe. (Perl 5.20 unterstützt Unicode 6.3.)

Mit dem Metaobjektprotokoll dauert es wesentlich länger, da man gerade im zweiten Anlauf bestrebt ist, einen Weg zu gehen, der möglichst wenige Umbauten der Interna verlangt. Moose samt Class::MOP ändert sich seit mehreren Jahren kaum noch. Es hat längst einen Standard für Perl-Programmierer gesetzt, der bereits nach Python (elk), JavaScript (Joose) und Ruby (MooseX) portiert wird. Wie seinerzeit bei den regulären Ausdrücken enthält Moose kaum ein Detail, dass nicht bereits woanders (Lisp, OCaml ...) formuliert wurde. Auch hier popularisierte Perl lediglich eine nützliche und praxistaugliche Abwandlung – vielleicht die beste Nebenerscheinung der in dieser Kultur verbreiteten Experimentierbereitschaft.

Allein schon wegen des jährlichen Releasezyklus kann die Version 5.20 nur ein Teil einer Modernisierung von Perl 5 sein, die sich ohne zynischen Unterton oder Marktgeschrei so nennen darf, da sie sich seit 2007 stetig vollzieht und wesentliche Mängel aufgreift. Naheliegend ist das heranreifende Perl 6 das maßgebliche Vorbild für diesen deutlichen Wandel, für den weder die aktuelle Diskussion noch die die sommerlichen YAPCs einen Masterplan vorweisen. Trotzdem wäre es nicht zu spekulativ, für Perl 5.22 bis 5.26 Moose-artige Objekte, Rollen, benannte Parameter und ein im typenlosen Perl durchführbare Datentyp-Überprüfung (eventuell sogar eine Ausnahmebehandlung) für möglich zu halten.

Herbert Breunung
schreibt regelmäßig Artikel über Perl 5, Perl 6 und GUI-Programmierung und spricht darüber auch auf Konferenzen im In- und Ausland. Außerdem führt er ein freies Softwareprojekt und den Aufbau einer hypertextfähigen Perl-6-Dokumentation.
(ane)