Perl 6

Die Zukunft des Programmierens

Perl 6 für Perl 5-Programmierer

Trotz vieler Änderungen von p5 zu p6 ist auch vieles gleich geblieben, was es alt eingesessenen Perl 5-Programmieren sicher leichter macht Perl 6 zu lernen als anderen.

Das hier ist ein Versuch, die wichtigsten Änderungen zusammenzufassen, die häufigsten am Anfang, danach grob nach Kategorien.

Verschiedenes

use strict; ist per default angeschaltet, Ausnahme sind Einzeiler die mit perl -e '$code' ausgeführt werden.

Für Objektmethoden gibt es jetzt die .-Notation (anstelle von ->), die meisten Builtins sind jetzt Methoden:

my @list;
@list.push("foo");   # wie push @list, "foo"
"foo".print;         # wie print "foo";

Die alte Notation ist immer noch unterstützt.

Wenn man bei Methoden (also Funktionen, die zu einem Objekt gehören), die Klammern weglassen will, braucht man einen Doppelpunkt:

@list.push: 'foo';

Die print-Funktion hat eine Schwester, die ein Newline am Ende anhängt:say (auch schon in perl 5.10 vorhanden).

my $answer  = 7 * 6;
# alle folgenden Varianten sind äquivalent:
print $answer, "\n";
say $answer;
$answer.say;

Sigils, Arrays und Hashes

Wie bisher fangen Skalare mit $, Arrays mit @ und Hashes mit % an.

Allerdings bleibt bei Arrays und Hashes der Sigil beim Zugriff auf Elemente erhalten:

my @list = 1, 3, 5, 1, 2;
my %hash = (
    'foo' => 'bar',
    'baz' => 'qox',
    );
@list[0];        #früher: $list[0]
%hash{'foo'};    #früher: $hash{'foo'}
%hash<foo>;      #früher: $hash{foo}

Außerdem gibt es auch noch das Sigil & für Subroutinen, Closures und Regexes:

my $i = 1;
my &func :=  sub { return $i++; }
# und später
say func;

Das Autoquoting beim Zugriff auf Hashes funktioniert in geschweiften Klammern nicht mehr, statt dessen gibt es jetzt <...>:

my $str = "foo";
%hash{$str};     # wie bisher
%hash<foo>;      # anstatt %hash{foo}
%hash{'foo'};    # wie bisher

Man kann jetzt auch auf Elemente von Array- und Hashreferenzen ohne den Pfeil-Operator zugreifen:

my $a = [1, 2, [3, 4, 5], 6];
$a[0];       # früher: $a->[0]
$a[2][0];    # früher $a->[0]->[0] oder $a->[0][0]

my $b = { 'a' => 'b', 'c' => [1, 2, 3]};
$b{'a'};     # früher $b->{a}
$b<a>;       # das gleiche
$b<c>[0];    # früher: $b->{c}[0] oder $b->{c}->[0]

Die Anzahl der Elemente eines Arrays bekommt man nun mit elems:

# man beachte, dass hier keine Klammern mehr nötig sind:
my @list = 1, 2, 4, 5;

my $num = @list.elems;            # früher: scalar @list;
$num = elems @list;               # das gleiche
my $last_index = @list.end;       # früher: $#list
my $last_elem = @list[@list.end]; # früher: $list[$#list]
$last_elem = @list[*-1];          # früher: $list[-1]

# funktioniert immer noch:
if @list < 3 {
	...
}

qw() wird durch <...> ersetzt und hat auch eine interpolierende Form:

my @list = <bla blubb>;     # früher: qw(bla blubb)
my $str = "bla blubb";
my @l2 = <<foo $str bar>>;  # liefert ['foo', 'bla', 'blubb', 'bar'];

Twigils

Globale Variablen (und ein paar andere) haben ein sekundäres Sigil, Twigil genannt:

%*ENV               # früher %ENV
@*ARGS              # früher @ARGV
$*IN, $*OUT, $ERR   # früher STDIN, STDOUT, STDERR

# zur Compilezeit bekannte Symbole:
$?LINE          # früher __LINE__
$?FILE          # früher __FILE__
$?PACKAGE       # früher __PACKAGE__

# Implizit deklarierte Variablen haben ein ^ als Twigl:
# in absteigender Reihenfolge sortieren:
sort {$^b <=> $^a}, @list # bisher: sort {$b <=> $a} @list

Kontrollstrukturen

if und unless benötigen jetzt keine Klammern mehr um die Bedingung:

if $a < 0 {         # if ($a < 0) {
    say "Negativ";  #   print "Negativ\n";
}                   # }

Sollte man trotzdem Klammern verwenden, muss nach dem if ein Leerzeichen stehen, sonst wird die Funktion namens if aufgerufen (sofern vorhanden).

foreach und for wird zu for, die C-artige for-Schleife zu loop:

my @nums = 1, 2, 3, 23, 42;

for @nums -> $i {
    # tolle Berechnung mit $i hier
}

# Iterationsvariablen sind per Default read only.
# Ändern kann man das so:

for @nums -> $i is rw {
    # tolle Berechnung mit $i hier
}
# Alternativ gäbe es noch 'is copy'

# über Hashes iteriern:
for %hash.kv -> my $key, $val {
    say "$key corresponds to $val";
}

# C-Style for-Schleife:
loop (my $i = 0; $i < 10; $i++){
    # tolle Berechnung mit $i hier
}

while bleibt unverändert (außer dass es auch keine runden Klammern mehr um die Bedingung braucht), eval { ... } ist jetzt try { ... }.

Die Standardvariable $_

for-Schleifen ohne eine explizite Variable benutzen auch weiterhin $_ als Standard, allerdings wird sie niemals von Funktionen wie print als Default verwendet. Dazu braucht man ein .print. Das gleiche gilt für Funktionen wie uc und lc:

                 # früher:
for $*IN.lines { # while (<STDIN>){
    .uc.say;     #     print uc;
}                # }

Allerdings gibt es auch noch eine andere Möglichkeit: mit dem Twigil ^ kann man Variablen mit beliebigen Namen verwenden, und man kann sogar mehrere davon in einem einer Schleife verwenden und damit mehrere Variablen auslesen:

for 1 .. 4 {
    say $^bla, ": ", $^blubb;
# Ausgabe: 
# 1: 2
# 3: 4

Dabei wird den Variablen, die mit $^ anfangen, in alphabetischer Reihenfolge die Werte zugewiesen:

for 1 .. 4 {
    say $^foo, ": ", $^bar;
}
# Ausgabe:
# 2: 1
# 4: 3

Funktionen

Funktionen werden immer noch mit sub-Keyword deklariert, und können jetzt Parameterdeklarationen enthalten wie in den meisten anderen Programmiersprachen.

sub fakultaet(Int $n){
    return  reduce {$^a * $^b}, 1 .. $n;
    # reduce reduziert Listen mit der angegbenen Funktion
}

# immer noch möglich:
sub fakultaet2 {
    return  reduce {$^a * $^b}, 1 .. @_[0];
}

# Parameter können auch beschreibbare Aliase sein:
sub swap($a is rw, $b is rw){
    $a, $b = $b, $a;
}

Typen und ref

Perl 6 erlaubt es, Typen für Variablen zu deklarieren, es ist aber weiterhin optional. Das funktioniert auch mit selbstdeklarierten Typen, also Klassen.

my Int $ganzzahl;
my Num $zahl;
my Complex $complexe_zahl;
my Str $string;
my Str @a; 		    # Array mit Strings
my Array of Str @b 	# Array von Arrays mit Strings

class Foo {
    # Defintionen hier
}

my Foo $foo;

Die Funktion ref gibt es nicht mehr, dafür gibt es als Ersatz zwei Möglichkeiten:

my $str = "foo";
say $str.WHAT;		# Ausgabe "Str"

# Ueberprüfung, ob etwas einen bestimmten Typ hat:
say "Das ist ein String" if $str ~~ Str

Die zweite Version verwendet den sogenannten Smart Match Operator ~~, das auf für Regular Expressions verwendet wird. Diese Form sollte man verwenden, wenn man überprüft, ob etwas einen bestimmten Typ hat, da das auch Vererbung berücksichtigt, d.h. sowohl [1, 2] ~~ Array als auch [1, 2] ~~ List liefern True zurück.

Regexes

Perl 5 Regexes können in Perl 6 immer noch verwendet werden, aber es gibt auch viele Erweiterungen. Darüber gibt es eine eigene Seite: Regexes und Rules.

Strings

Strings in Perl 6 sind Unicode-Strings, die auf verschiedenen Ebenen gleichzeitig existieren können (bytes, code points, graphemes und charlingua).

Deswegen gibt es kein length mehr, weil nicht defniert ist, was damit gemeint ist. Stattdessen kann mit bytes die Anzahl der Bytes abfragen, mit chars die Anzal der Zeichen , mit codes die Anzahl der Codepoints, mit graphs die Anzahl der Grapheme.

So haben z.B. die deutschen Umlaute in UTF-8 zwei Bytes, aber je nur ein char, code und graph.

Dagegen können z.B. Ligaturen, also aus zwei Buchstaben zusammen gesetzte Zeichen, nur ein grapheme aber zweide codepoints haben.

Wenn man mit split Strings aufspalten will, muss man die Regex als rx/.../ übergeben:

"foo bar  baz".split: rx/\s+/;

Zusätzlich gibt es comb (von "to comb", durchkämmen), das alle Teile eines Strings zurückliefern, die auf eine regex passen - quasi ein invertiertes split.

Mit index und rindex kann man das erste und letzte Vorkommen eines substrings finden:

"foo".index("o");   # ist 1
"foo".rindex("o");  # ist 2

Strings werden jetzt mit ~ zusammengefügt (früher: .), die Tilde soll zwei Arme symbolisieren, die nach den beiden Enden der Strings greifen sollen:

my $name = "Perl";
my $version = 6;
my $lang = $name ~ " " ~ $version;

Gleichzeitig erzwingt die Tilde Stringkontext:

my @a = "foo", "bar;
my $str = ~@a;      # "foo bar"

I/O

Aus der <$fh>-Notation zum Lesen aus Filehandles ist $fh.lines geworden, und das Zeilenende wird jetzt per default automatisch entfernt ("autochomp").

my $fn = "foo.txt";
my $file = $fn.open err die "Can't read '$fn': $!";

for $file.lines -> $line {
    # hier die Zeile verarbeiten
}

# oder:
my $ganze_datei = $file.slurp;

$file.close;

Einzelne Zeilen liefert $handle.get.

Die empfohlene Art, eine Datei zeilenweise zu lesen ist jetzt eine for-Schleife. Und das bedeutet nicht wie in Perl 5, dass erst die gesamte Datei gelesen wird und dann darüber iteriert wird, sondern die Werte werden dann aus der Datei gelesen, wenn sie gebraucht werden.

Das ist möglich, weil in Perl 6 Listen per default lazy sind, was es auch möglich macht, unendliche Listen zu verwenden: (1 .. *)[1].

Und so geht das Schreiben in Dateien:

my $fn = "foo.txt";
my $file = open($fn, :w) err die "Can't open '$fn' for writing: $!";

$file.say: "Erste Zeile der Datei";
# oder:
say $file: "Zweite Zeile";
print $file: 'Diese Zeile hat kein \n am Ende';
$file.close;

Man beachte, dass aus print $fh $text jetzt print $fh: $text (mit einem zusätlichen Doppelpunkt) geworden ist, da print u.A.eine Methode der zuständigen IO-Klasse ist.

File Tests

File test, wie z.B. -e $filename werden jetzt mit dem Smart Match-Operator durchgeführt, und können sowohl auf Strings (als Dateinamen) als auch auf Filehandles angewandt werden:

my $fn = "/etc/passwd";

if $fn ~~ :e {
    say "$fn existiert";
} else {
    say "Wohl kein Unix...";
}

Der :s-Filetest liefert True zurück, wenn die Datei nicht leer ist, und im numerischen Kontext gibt er die Länge der Datei in Byte zurück.

system und exec

system, also das Ausführen von anderen Programmen, heißt jetzt run:

my $status = run("mutt -f /path/to/mbox");
# oder
$status = run("mutt", "-f", "/path/to/mbox");
if $status {
    say "Mutt lief erfolgreich";
} else {
    say "Fehler beim ausführen von mutt, Rückgabewert: $status";
}

Der Rückgabewert, hier $status, ist im Boolean context True, wenn das Programm 0 zurückgeliefert hat, und nicht False wie normale Integer.

exec heißt jetzt runinstead.

Neue Features

Eine ganze Reihe neuer Features gibt es in Perl 6, die es in Perl 5 in dieser Form noch gar nicht gab.

Metaoperatoren

Metaoperatoren verwenden andere Operatoren, um sie z.B. auf Listen anzuwenden:

my @a = 1 .. 4;
my $sum = [+] @a;       # $sum = 10
# Aequivalent:
$sum = reduce &infix:<+>, @a;
$sum = reduce {$^a + $^b}, @a;
$sum = reduce -> $a, $b { $a + $b }, @a;

# Hyperoperatoren:
my @b = <4 3 2 1>
my @sum = @a »+« @b      # @sum = 5, 5, 5, 5
# kann man auch als >>+<< schreiben

my @b_plus_one = @b »+ 1;

Der Hyperoperator »...« wendet den Operator in der Mitte paarweise auf die Elemente zweier Listen an. Wenn man auf einer Seite keine Liste sondern einen Skalar angibt, muss man die entsprechende Hälfte des Hyperoperators weglassen.

Anstatt der Unicode-Zeichen » und « kann man auch doppelte Kleiner- und Größerzeichen >>...<< benutzen.

Hyper- und Reduktionsoperator führen ihre Operationen auf Multi-Prozessor-Systemen parallelisiert aus, wenn der Interpreter bzw. Compiler entsprechend konfiguriert wird.

Verschiedenes (2)

wantarray wird want

Die Funktion wantarray ist dem allgemeineren want-Builtin gewichen.

Die alte Funktionalität bekommt man mit want.list oder alternativ want ~~ List.

Allgemein kann man mit want ~~ $typ abfragen, ob der Aufrufer den Typ $typ erwartet, und mit want.count. Mehr dazu im Abschnitt Kontexte.

Data::Dumper

Data::Dumper ist jetzt überflüssig, dafür gibt es die eingebaute Methode perl:

my %foo = "a" => "b", "c" => <d e f>;
say %foo.perl;
#Ausgabe: {("a" => "b"), ("c" => ("d", "e", "f"))}

perl gibt Perl-Code aus, der das entsprechende Konstrukt erzeugt.

Ausführen anderer Programmiersprachen

Mit eval können jetzt auch andere Programmiersprachen ausgeführt werden, insbesondere Perl 5, aber auch Yaml (liefert dann Datenstrukturen zurück), und je nach Compiler/Interpreter eventuell Java, Haskell, Ruby und andere:

eval("/* Java-Code hier, der einen Wert liefert */", :lang(Java));

Die Flexibilität der Datenstrukturen, die von der "Fremdsprache" zurückgegeben werden können, hängt sicher von der entsprechenden Sprache und dem Interface ab, aber zumindest Perl 5 sollte beliebig komplexe Datenstrukturen (inklusive Objekte) zurückliefern können.

Pointy Blocks

Pointy Blocks sind eine neue Möglichkeit, anonyme Codeblöcke zu schreiben:

my $square = -> $a { $a * $a };
say $square(4);
# Ausgabe: 16

say $square.WHAT
# Ausgabe: Block

Man beachte, dass die for-Schleife, wie sie vorher vorgestellt wurde, eine pointy block benutzt:

for 1 .. 5 -> $a {
    say $a;
}

Alternativ können auch self declaring formal parameters verwendet werden, also Variablen mit dem $^-Sigil:

for 1 .. 5  {
    say $^number;
}