#Private attribute example
class C {
has $!w; #private attribute
multi method w { $!w } #getter method
multi method w ( $_ ) { #setter method
warn “Don’t go changing my w!”; #some side action
$!w = $_
}
}
my $c = C.new
$c.w( 42 )
say $c.w #prints 42
$c.w: 43
say $c.w #prints 43
#but not
$c.w = 44
Cannot modify an immutable Int (43)
so weit, so vernünftig und dann
#Public attribute example
class C {
has $.v is rw #public attribute with automatic accessors
}
my $c = C.new
$c.v = 42
say $c.v #prints 42
#but not
$c.v( 43 ) #or $c.v: 43
Too many positionals passed; expected 1 argument but got 2
Ich mag die Unmittelbarkeit der Zuweisung '=', aber ich brauche die Leichtigkeit, Nebenaktionen durchzuführen, die mehrere Methoden bieten. Ich verstehe, dass dies zwei verschiedene Welten sind und dass sie sich nicht vermischen.
ABER - Ich verstehe nicht, warum ich nicht einfach $ cv (43) gehen kann, um ein öffentliches Attribut festzulegen
- Ich habe das Gefühl, dass Raku mich dazu anleitet, diese beiden Modi nicht zu mischen - einige Attribute privat und andere öffentlich und dass der Druck auf die Methodenmethode gerichtet ist (mit einigen: Zucker aus dem Dickdarm) - ist dies die Absicht von Rakus Design?
- Vermisse ich etwas
is rw
angegeben ist. Durch die Rückgabe eines Proxys wird die Anzahl der zulässigen Parameter auf dem Accessor nicht geändert.= foo
als auch.(foo)
einstellen) und in beiden Fällen Nebenwirkungen zu ermöglichen (aber nicht, wenn nur abgerufen): tio.run/…Antworten:
Es ist fair zu sagen, dass Raku in diesem Bereich nicht völlig unbefangen ist. Ihre Frage berührt zwei Themen in Rakus Design, die beide eine kleine Diskussion wert sind.
Raku hat erstklassige l-Werte
Raku nutzt L-Werte in Hülle und Fülle als erstklassige Sache. Wenn wir schreiben:
Die generierte Methode ist:
Das
is rw
hier zeigt an, dass die Methode einen l-Wert zurückgibt, dh etwas, dem zugewiesen werden kann. Wenn wir also schreiben:Dies ist kein syntaktischer Zucker: Es ist wirklich ein Methodenaufruf, und dann wird der Zuweisungsoperator auf das Ergebnis angewendet. Dies funktioniert, da der Methodenaufruf den
Scalar
Container des Attributs zurückgibt , dem dann zugewiesen werden kann. Man kann die Bindung verwenden, um dies in zwei Schritte aufzuteilen, um zu sehen, dass es sich nicht um eine triviale syntaktische Transformation handelt. Zum Beispiel:Würde dem Objektattribut zuweisen. Derselbe Mechanismus steckt hinter zahlreichen anderen Funktionen, einschließlich der Listenzuweisung. Zum Beispiel:
Konstruiert ein Konstrukt,
List
das die Container$x
und enthält$y
, und dann iteriert der Zuweisungsoperator in diesem Fall jede Seite paarweise, um die Zuweisung durchzuführen. Dies bedeutet, dass wir dortrw
Objekt-Accessoren verwenden können:Und alles funktioniert ganz natürlich. Dies ist auch der Mechanismus für die Zuweisung von Arrays und Hashes.
Sie können auch
Proxy
einen L-Wert-Container erstellen, in dem Sie das Lese- und Schreibverhalten steuern können. So können Sie die Nebenaktionen in setzenSTORE
. Jedoch...Raku fördert semantische Methoden gegenüber "Setzern"
Wenn wir OO beschreiben, tauchen häufig Begriffe wie "Kapselung" und "Verstecken von Daten" auf. Die Schlüsselidee dabei ist, dass sich das Zustandsmodell innerhalb des Objekts - dh die Art und Weise, wie es die Daten darstellt, die es zur Implementierung seines Verhaltens (der Methoden) benötigt - frei entwickeln kann, beispielsweise um neue Anforderungen zu bewältigen. Je komplexer das Objekt ist, desto befreiender wird es.
Getter und Setter sind jedoch Methoden, die eine implizite Verbindung zum Status haben. Während wir vielleicht behaupten, dass wir das Ausblenden von Daten erreichen, weil wir eine Methode aufrufen und nicht direkt auf den Status zugreifen, ist meine Erfahrung, dass wir schnell an einem Ort landen, an dem externer Code Sequenzen von Setter-Aufrufen ausführt, um eine Operation zu erreichen - das heißt eine Form des Merkmals Neid Anti-Muster. Und wenn wir das tun , ist es ziemlich sicher, dass wir eine Logik außerhalb des Objekts haben, die eine Mischung aus Getter- und Setter-Operationen ausführt, um eine Operation zu erreichen. Eigentlich sollten diese Operationen als Methoden mit einem Namen verfügbar gemacht worden sein, der beschreibt, was erreicht wird. Dies wird noch wichtiger, wenn wir uns in einer gleichzeitigen Umgebung befinden. Ein gut gestaltetes Objekt ist an der Methodengrenze oft recht einfach zu schützen.
Das heißt, viele Verwendungen von
class
sind wirklich Datensatz- / Produkttypen: Sie existieren, um einfach eine Reihe von Datenelementen zu gruppieren. Es ist kein Zufall, dass das.
Siegel nicht nur einen Accessor generiert, sondern auch:class Point { has $.x; has $.y; }
kann als instanziiert werdenPoint.new(x => 1, y => 2)
), und rendert dies auch in der.raku
Dumpingmethode..Capture
, was bedeutet, dass wir es bei der Destrukturierung verwenden können (zsub translated(Point (:$x, :$y)) { ... }
. B. ).Welches sind die Dinge, die Sie möchten, wenn Sie in einem prozeduraleren oder funktionaleren Stil schreiben und
class
als Mittel zum Definieren eines Datensatztyps verwenden würden.Das Raku-Design ist nicht für clevere Dinge in Setzern optimiert, da dies als schlecht zu optimierend angesehen wird. Es geht über das hinaus, was für einen Datensatztyp benötigt wird. In einigen Sprachen könnten wir argumentieren, dass wir die zugewiesenen Aufgaben validieren möchten, aber in Raku können wir uns dafür
subset
Typen zuwenden . Wenn wir wirklich ein OO-Design erstellen, möchten wir gleichzeitig eine API mit aussagekräftigen Verhaltensweisen, die das Zustandsmodell verbirgt, anstatt in Getter / Setter zu denken, die dazu führen, dass die Zuordnung fehlschlägt Daten und Verhalten, was ohnehin ein wichtiger Punkt bei OO ist.quelle
Proxy
s zu warnen (obwohl ich es vorgeschlagen habe ha). Das einzige Mal, dass ich sie für schrecklich nützlich befunden habe, ist für michLanguageTag
. Intern wird die$tag.region
gibt ein Objekt vom TypRegion
(wie es intern gespeichert ist), aber die Realität ist, es ist unendlich viel bequemer für die Menschen zu sagen ,$tag.region = "JP"
über$tag.region.code = "JP"
. Und das ist wirklich nur vorübergehend, bis ich einen Zwang ausStr
dem Typ ausdrücken kann , z. B.has Region(Str) $.region is rw
(der zwei separate geplante Funktionen mit niedriger PrioritätNun, das liegt wirklich am Architekten. Aber im Ernst, nein, das ist einfach nicht die Standardarbeitsweise von Raku.
Jetzt wäre es durchaus möglich, ein
Attribute
Merkmal im Modulraum zuis settable
erstellen, etwa eine alternative Zugriffsmethode, die einen einzelnen Wert zum Festlegen des Werts akzeptiert. Das Problem dabei ist, dass es meiner Meinung nach im Grunde zwei Lager auf der Welt gibt, in denen es um den Rückgabewert eines solchen Mutators geht: Würde er den neuen Wert oder den alten Wert zurückgeben?Bitte kontaktieren Sie mich, wenn Sie daran interessiert sind, ein solches Merkmal im Modulraum zu implementieren.
quelle
Ich vermute derzeit, dass Sie gerade verwirrt wurden. 1 Bevor ich darauf eingehe, beginnen wir noch einmal mit dem, worüber Sie nicht verwirrt sind:
Sie können all diese Dinge tun. Das heißt, Sie verwenden
=
Zuweisung und mehrere Methoden und "gehen einfach$c.v( 43 )
" gleichzeitig, wenn Sie möchten:Eine mögliche Quelle der Verwirrung 1
has $.foo is rw
Generiert hinter den Kulissen ein Attribut und eine einzelne Methode wie folgt:Das obige ist jedoch nicht ganz richtig. Angesichts des Verhaltens, das wir sehen, wird die automatisch generierte
foo
Methode des Compilers irgendwie so deklariert, dass jede neue Methode mit demselben Namen sie stillschweigend beschattet . 2Wenn Sie also eine oder mehrere benutzerdefinierte Methoden mit demselben Namen wie ein Attribut möchten, müssen Sie die automatisch generierte Methode manuell replizieren, wenn Sie das Verhalten beibehalten möchten, für das sie normalerweise verantwortlich ist.
Fußnoten
1 Siehe jnthns Antwort für eine klare, gründliche und maßgebliche Darstellung von Rakus Meinung über private und öffentliche Getter / Setter und was es hinter den Kulissen tut, wenn Sie öffentliche Getter / Setter deklarieren (dh schreiben
has $.foo
).2 Wenn eine automatisch generierte Accessormethode für ein Attribut deklariert wurde
only
, würde Raku vermutlich eine Ausnahme auslösen, wenn eine Methode mit demselben Namen deklariert würde. Wenn es deklariert wurdemulti
, sollte es nicht beschattet werden, wenn die neue Methode ebenfalls deklariert wurdemulti
, und sollte eine Ausnahme auslösen, wenn nicht. Daher wird der automatisch generierte Accessor weder mitonly
noch deklariert,multi
sondern auf eine Weise, die eine stille Abschattung ermöglicht.quelle