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}