Perl 6
Perl 6 Tutorial Part 6: Revision 4


Objects and Roles

Willkommen und hereinspaziert mein Damen und Herren. Hier erleben sie Menschen -
Tiere - Sensationen, so etwas hat die Welt noch nicht gesehen. Und fürwahr das
Objektsystem von Perl 6 übertrifft in seiner Ausgereiftheit und Vielseitigkeit
alles bisher aus der Praxis Bekannte. Hier gab es einige der stärksten Änderungen
gegenüber Perl 5. Vieles davon ist jedoch heute bereits (leicht abgeändert) mit
Moose umsetzbar.

Das alte System

Die alte OOP bestach vor allem durch ihre Freiheit und daß sie mit minimalstem
syntaktischen Aufwand in das objektlose Perl 4 eingefügt wurde. Nur C<bless> (der
Befehl zur Erzeugung eines Perl 5-Objektes) kam extra deshalb dazu. Und dieser
Befehl bindet "lediglich" eine Referenz - meist eine Hashreferenz - an ein C<package>
und macht sie so zum Objekt. Damian Conway bewundert diesen "Taschenspielertrick",
und zeigt in seinem Buch I<"Object Oriented Perl">" die immensen Möglichkeiten des
"Tricks" auf. Larry hatte diesen "Trick" eigentlich von Python abgeschaut, es
aber seitdem bereut. Nicht nur Guido van Rossum hat mit der kürzlich erschienenen
Version 3 diese Art der OOP aus Python gestrichen, auch Perlkreise sehen dieses
Objektsystem derzeit als größte Schwachstelle der Perlsyntax. Es ist nicht nur für
viele Perlneulinge eine echte Hürde, sondern verstößt auch gegen Perls eigene Regeln.

Was Perl ausmacht, zeigt das alltägliche Grundbeispiel, das gleichzeitig den ersten
Babyschritten jedes Programmierers entspricht: C<print "Ich kann schon laufen!\n">.
Keine Systembibliothek wird importiert, keine C<main>-Routine ist notwendig, keine
Klasse muss drumherum gestrickt werden. Dem Programmierer wird weder Arbeit noch
Wissen abverlangt, das nicht unbedingt notwendig ist. In Perl 6 und 5.10 reicht
sogar C<say "Ich kann schon laufen!">, da die Bedeutung von Steuerzeichen sicher
nicht das Erste ist, was Anfänger lernen sollten. Wenn er jedoch eine einfache
Klasse schreiben will, braucht er einige Kenntnis von Namensräumen, Referenzen,
Hashes, C<bless> und Perls eigenwilliger Art der Parameterübergabe, wie es das
folgende Beispiel zeigt:

Perl 5:

package Heart::Gold;

sub new {
bless {speed => 0 }, shift;
}

sub speed {
my $self = shift;
my $speed = shift;
if ($speed) { $self->{speed} = $speed }
else { $self->{speed} }
}

sub stop {
my $self = shift;
$self->{speed} = 0;
}

Benutzt wird diese Klasse wie folgt:

Perl 5:

my $ufo = Heart::Gold->new();
# "gib Gas, Scotty"
$ufo->speed('7c');
# "alle Maschinen halt"
$ufo->stop();

Für gute Perl-Programmierer ist das kein Problem, aber sie freuen sich über Wege,
das Gleiche mit weniger Tippaufwand zu erreichen, wie die Existenz von I<Moose>,
I<Class::Accessor::Fast> und mindestens einem Dutzend weiterer Module beweist.
Tipparbeit zu reduzieren ist ebenfalls ein wichtiges Perlziel.

... und nun das Neue

Eine fast funktionsgleiche Klasse sähe in Perl 6 so aus:

Perl 6:

class Heart::Gold {
has $.speed;
method stop { $speed = 0 }
}

Das ist kurz, klar und weniger fehleranfällig. Bereits der erste Befehl kündigt
unmissverständlich an, daß hier eine Klasse erzeugt wird (deklarativer Syntax).
C<class> ist (genau genommen) ein I<Blockmodifikator>, der dem nachfolgendem
(in geschweiften Klammern stehenden) lexikalischen Bereich einen Namen zuweist
(wie C<package>) und daraus ein Protoobjekt erzeugt aus dem nachfolgend Objekte
geklont werden können. Perl 6 hat eine prototyp-basierte OOP, ähnlich Javascript.
Möglich ist auch die Schreibweise C<class Heart::Gold;> ohne geschweifte Klammern.
Dann nimmt der Interpreter jedoch an, daß alles bis zum Dateiende zu jener Klasse
gehört.

Der zweite Befehl (C<has>) definiert ein Attribut der Klasse. Attribute sind die
Daten die zu jedem Objekt gehören und die von außen unsichtbar sind. (Das wäre in
Perl 5 nicht einfach möglich.) Auf manche dieser Daten ist aber der Zugriff von
außen gewünscht. In vielen Sprachen werden deshalb Methoden geschrieben die Werte
von und zu den Attributen weiterleiten. In Perl 6 kann der Programmierer mit der
Twigil (dem Punkt nach "$") die öffentlichen Attribute markieren und die Methode
gleichen Namens, die einen Zugriff von außen erlaubt, wird automatisch erzeugt.
Möchte man neben dem "Getter" auch einen "Setter" (Methode, die dem Attribut
einen Wert zuweist), muss man das Attribut mit dem Trait (Eigenschaft die während
der Kompilierung unabänderlich festgelegt wird - zu deutsch: Charakterzug) C<rw>
versehen.

has $.speed is rw;

C<rw> steht für "read/write" (lesen/schreiben) und wird zu vielen Gelegenheiten
angewendet, z.B. wenn eine Datei für Lese- und Schreibzugriff geöffnet wird.
Der erzeugte Setter ist eine Lvalue-Routine. Ihm kann also zugewiesen werden.
Das erlaubt nicht nur einen einfachen, intuitiven Umgang, da Getter und Setter
sich wie Variablen verhalten, es vermeidet auch hinterhältige Fallen, die eine
selbstgeschriebene, kombinierte Getter/Setter-Methode aufstellt. Sie sind auch
angenehmer im Umgang als die Getter/Setter, die in unserem Beispiel "GetSpeed"
und "SetSpeed" oder "get_speed" / "set_speed" heißen würden. Sind alle oder die
Mehrheit der Attribut "rw", ist es kürzer zu schreiben:

class Heart::Gold is rw { ...

Soll ein Attribut privat sein, wird es mit "!" (Punkt mit Wand darüber) markiert.
Der automatisch erzeugte Getter ist dann eine private Methode, die nur innerhalb
der Klasse benutzt werden kann. Ohne Twigil wird keine Zugriffsmethode erzeugt.
Das ist auch nicht notwendig, da jedes Attribut innerhalb der Klasse einer
lexikalisch lokalen Variable entspricht. Deshalb kann es auch kein gleichnamiges
privates und öffentliches Attribut geben. (Innerhalb der Beispielklasse greift
C<$.speed>, C<$!speed> und C<$speed> auf das gleiche Attribut zu.) Eine lokale
Klassenvariable (kein Attribut) is jedoch möglich, wenn auch nicht empfehlenswert,
es der Autor wünscht sich eine Variable die alle Instanzen der Klasse teilen.
Dies hieße ebenfalls C<$speed>, während das Attribut weiterhin mit C<$!speed>
in der Klasse erreichbar wäre, in der es definiert wurde. Die Befehle C<my> und
C<our> haben bei I<Accessoren> (Getter/Setter) keinen Effekt.

Der dritte neue Befehl ist C<method>, der selbsterklärend (anstatt C<sub>) eine
Methode erzeugt (deklarativer Syntax). Methoden sind Routinen eines Objektes.
Objekte wurde ja "erfunden" um Daten mit zugehörigen Routinen zusammenzufassen.
Sie werden nun mit C<$objekt.methode()> anstatt C<$objekt->methode()> aufgerufen.
Das spart ein Zeichen zu tippen, liest sich leichter und ist verbreiteter Standard.
Mit diesem Wissen wird klar, warum C<$.speed> der Befehl ist, eine gleichnamige
Methode zu erzeugen.

Verwendung von Objekten

Ein weiterer großer Unterschied zum Perl 5-Beispiel ist das Fehlen der Methode
C<new>. Die wird automatisch erzeugt, da der Interpreter auf alle nötigen
Informationen zugreifen kann. Bei Bedarf kann allerdings eine eigene C<new>-Methode
oder ebenso eigene Getter/Setter erstellt werden, welche die autogenerierten
Methoden überschreiben. Die Verwendung einer Klasse sieht damit so aus:

my $ufo = Heart::Gold.new();
$ufo.speed ='5c';
$ufo.stop;
# 0, da numerischer Kontext erzwungen wird
say + $ufo.speed;

Die ersten beiden Schritte ließen sich sogar noch zusammenfassen:

my $ufo = Heart::Gold.new( speed =>'5c'); #oder
my Heart::Gold $ufo .= new( speed =>'5c');

Praktischerweise kann die autogenerierte C<new>-Methode auch Attribute befüllen.
Im zweiten Fall wird der Variable die Klasse (Protoobjekt) als Datentyp zugewiesen.
Gemäß der Logik selbszuweisender Operatoren (z.B."+=") wird .new auf den Aufrufer
angewendet und so entsteht ebenfalls ein Objekt. Diese Art der "selbstzuweisenden"
Methoden wird I<Mutator> genannt (z.B. auch C<@array .= sort>). Auf diese Weise
kann man ebenso Objekte durch Klonen erzeugen:

# mit alternative Paar-Konstruktor
my $arv = $ufo.clone( :speed<2c> );

Natürlich kann ein ganzer C<%Hash> an Werten bei der Erzeugung übergeben werden.
Dabei sieht die Syntax etwas wie Perl 5 aus, da es immer noch eine Methode namens
C<bless> gibt, die ähnlich dem altem C<bless>-Befehl funktioniert.

my $klasse = class Heart::Gold { ... }
my %werte = :speed<20c>;
$efv = $klasse.bless( %werte );

Eine Kleinigkeit fehlt aber noch: Wie erklärt man eine Methode C<protected>, also
nicht über ein Objekt aufrufbar? (C<$objekt.methode()>) Genau wie man seit Perl 5
Variablen auch in einem Namensraum von außen abschottet, mit C<my>.

class Heart::Gold {
my method secret_maneuver { ... }
}
my $ufo = Heart::Gold.new;
# Laufzeiterror:
$ufo.secret_maneuver;

Mit C<my> lassen sich auch lexikalisch lokale Klassen deklarieren. Die benötigt
man zum Beispiel, wenn Attribute selber Objekte sind, deren Klasse geheim bleiben
soll. In diesem Beispiel:

class Heart::Gold {
class Movement { ... }
has int $.length where {$_ >= 0};
has Movement $drive;
my method secret_maneuver { ... }
}

hat ein Attribut einen vor Ort erstellten Subtyp (siehe letzte Folge), das andere
ist ein Objekt (Instanz der Klasse C<Movement>). Da diese Klasse nicht mit C<my>
als lexikalisch lokal deklariert wurde, ließen sich außerhalb von C<Heart::Gold>
ebenso C<Movement>-Objekte erstellen. Auch prozeduraler Zugriff auf den Namensraum
C<Movement> wäre dann möglich.

Delegier die Arbeit

Eine Klasse C<Movement> ist sinnvoll, da solch ein fortschrittliches Fluggerät,
sicher sehr viele Eigenschaften (Attribute) und Funktionen (Methoden) besitzt.
Sie alle in eine Klasse zu schreiben ginge zu Lasten der Nachvollziehbarkeit.
Unser Attribut C<$drive>, welches das Antriebsaggregat repräsentiert, ist also
ein Objekt, dessen Methoden nun Informationen über Geschwindigkeit und ähnliches
herausgeben. Es erscheint als müsste nun doch ein I<Getter> C<$.speed> geschrieben
werden, welcher die Methode C<$drive.speed> aufruft und ihr Ergebins weiterleitet.
Aber auch hierfür gibt es eine kurze Schreibweise die genau dies automatisch tut.

class Heart::Gold {
has Movement $drive handles 'speed';

Dieses Konzept wird in der OOP-Welt I<Delegation> genannt und wird natürlich wie
fast alles bekannte auch von Perl 6 angeboten. Nich umsonst heißt das neue Motto:
"all your paradigms are belong to us".

Regelung der Erbschaft

Somit erübrigt sich auch die Frage: "Gibt es Mehrfachvererbung in Perl 6?", doch
beginnen wir mit der Einfachen. Klassen vererben einander all ihre Attribute und
Methoden. Das nennt man auch "ableiten". So kann eine einmal geschriebene Klasse
wiederverwendet und Schritt für Schritt erweitert werden. Besitzt die Elternklasse
dabei eine Methoden, welche der Sprössling auch definiert, so überschreibt er
damit die geerbte Methode. Ich könnte eine einfache Klasse schreiben, die den Flug
von Pollen oder einer Pusteblume wiedergibt. Von dieser könnte eine nächste erben
und einige Methoden zufügen, welche Manöver eines Heißluftballons beschreiben,
u.s.w bis wir in sechster oder siebenter Generation einen Ufoantrieb haben der
auch auf die Zeitverschiebungen bei Interdimensionsreisen eingehen kann.

class UfoDrive is AdvancedRocket {
...
}

Da die "Heart of Gold" bekanntermaßen einen Unwahrscheinlichkeitsantrieb besitzt,
der die unwahrscheinlichen Beträge auf den Rechnungen des integrierten Restaurants
nutzt, muss noch von einem solchen alles vererbt werden.

class UfoDrive is AdvancedRocket is Restaurant { ... }

Das ist eine Kurzschreibweise für:

class UfoDrive {
is AdvancedRocket;
is Restaurant;
...
}

Es sei anzumerken, daß die Klasse I<Restaurant> gleichnamige Methoden der Klasse
I<AdvancedRocket> "überschreibt". In Java können Klassen als "final" markiert
werden. Das ist in Perl 6 nicht direkt vorgesehen, jedoch kann in einem Programm
mit dem Pragma C<use oo :final;> angewiesen werden, alle Klassen automatisch als
"final" zu betrachten. Ausnahmen können mit

{
use class :nonfinal;
class UfoDrive { ... }
class Heart::Gold { ... }
}

oder

class Insect is nonfinal { ... }

angegeben werden. Einzelne Methoden, die nicht vererbt werden sollen, können als
C<submethod> deklariert werden, die ebenfalls mit einem C<my> davor nicht über
ein Objekt oder den Namensraum aufrufbar sind.

Rollen die ich spiele

Eine Rolle wird definiert wie eine Klasse, nur mit dem Schlüsselwort C<role>
anstatt C<class>.

role Blinken {
has @.lichter;
method blinken { ... }
}

Von einer C<role> kann aber kein Objekt erzeugt (geklont) werden. Wozu dient dann
eine C<role>? Die Praxis hat gezeigt, daß mit Klassen allein der Code nicht immer
einfach wiederverwendbar ist. Schnell kam es zu Bäumen von Vererbungshierarchien.
Was aber tun, wollten zwei "Äste eines Baums" wenige Methoden gemeinsam verwenden?
Selbst Sprachen, die Mehrfachvererbung kennen (nicht Java und C#) erzeugen damit
neue Probleme, die mit Rollen vermieden werden können. Sie sind auch syntaktisch
natürlicher und bieten sogar mehr Sicherheit ungewollte Methoden zu überlagern als
Rubys I<Mixins>. Rollen werden während der Kompilierung in Klassen "eingebunden",
wenn es die Klasse fordert.

class Heart::Gold does Blinken {

oder

class Heart::Gold {
does Blinken;
...
}

Wie zu erahnen, besitzt nun auch die Klasse I<Heart::Gold> die Methode I<blinken>.
Roles können jedoch nicht Methoden einer Klasse überlagern. Versuchen zwei Roles
gleichnamige Methoden zu importieren gibt es einen Fehler beim Kompilieren, es
sei denn die Klasse hatte eine gleichnamige Methode und der Konflikt muss nicht
gelöst werden. Perl 6 kennt Wege wie Roles und Klassen ihre Konflikte selbst lösen
können, dies würde aber den Rahmen überdehnen. Was jedoch jeder über Roles wissen
sollte, der ihre Verwendung in Betracht zieht, betrifft Attribute. Roles können
natürlich eigene Attribute mitbringen. Dies tat die Role im Beispiel mit:

has @.lichter; # oder:
has @!lichter; # oder:
my @!lichter; # für Klasse unsichtbar

Sie könnte aber auch Attribute der Klasse mitnutzen. Das meldet sie an mit:

has @lichter;

und der Kompiler prüft ob die Klasse dieses Attribut besitzt. Ein großer Nutzen
der Roles kommt auch von ihrer Fähigkeit sich auch zur Laufzeit einzubinden.

$ufo does Blinken;

Mehrere Roles müssen einzeln gebunden oder geklammert werden:

(($ufo does Blinken) does Tarnen);

Bei diesen Fähigkeiten, gibt es auch Bedarf zur Laufzeit zu prüfen, ob eine Role
in ein Objekt "eingemischt" wurde.

if $ufo.does(Blinken) { ... }

Was in unserem Falle wahr (Bool::True) ist. Ebenso kann geprüft werden ob ein
Objekt von einer Klasse abgeleitet wurde:

if $ufo.isa(Heart::Gold) { ... }

Dieser Syntax ähnelt sehr Perl 5, Folgendes ist jedoch neu, auch wenn Perl 5.10
bereits smartmatch kennt:

if $ufo ~~ Blinken { ... }
if $ufo ~~ Heart::Gold { ... }

Beide Fälle sind genauso wahr, da C<~~> eine große Tabelle hat und je nach Typ der
Parameter versucht das "Richtige" zu tun. Solches Verhalten erreicht man durch
C<multi>-Methoden (in vorigen Folge vorgestellt). Wenn I<Roles> I<Multimethoden>
importieren, dürfen diese gleichnamig sein, solange die Signaturen nicht gleich
sind.

Zeig mir dein Inneres

Objekte lassen sich in Perl 6 weitaus tiefer durchleuchten als es das Smartmatch
tut. C<$ufo.HOW> oder C<^Heart::Gold> erlaubt z.B. den Zugriff auf die Metaklasse
und C<Heart::Gold.^methods()> liefert beispielsweise alle Methoden der Klasse.
Doch an der Stelle möchte ich mich für dieses mal verabschieden, da die Methoden
zur Introspektion (hoffentlich bald vollständig) im Tutorial der Perl-Community.de
Wiki aufgelistet sind und ein Aufzählen einer API langweilig ist. Diese API ist,
im Gegensatz zu dem in dieser Folge vorgestellten Syntax, keinesfalls stabil und
endgültig, sodass ein Blick in den tabellarischen Index B des Tutorials sinnvoll
ist. Die nächste Folge wird behandeln, was Perl einst berühmt und berüchtigt
machte: die regulären Ausdrücke die jetzt wirklich regulär sind, sich nicht mehr
Ausdruck nennen und auch wie Objekte behandelt werden können.


Previous Chapter | Overview | Next Chapter


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: