#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)
jusqu'à présent, si raisonnable, puis
#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
J'aime l'immédiateté de l'affectation «=», mais j'ai besoin de la facilité de se lier aux actions secondaires fournies par les méthodes multiples. Je comprends que ce sont deux mondes différents, et qu'ils ne se mélangent pas.
MAIS - je ne comprends pas pourquoi je ne peux pas simplement aller $ cv (43) pour définir un attribut public
- Je sens que le raku me guide pour ne pas mélanger ces deux modes - certains attributs privés et certains publics et que la pression est vers la méthode (avec certains: le sucre du côlon) - est-ce l'intention du design de Raku?
- Suis-je en train de manquer quelque chose?
is rw
est spécifié. Le renvoi d'un proxy ne changera pas le nombre de paramètres autorisés sur l'accesseur.= foo
et.(foo)
pour le réglage) et permettre aux effets secondaires de se faire dans les deux cas (mais pas lorsqu'ils sont récupérés uniquement): tio.run/…Réponses:
Il est juste de dire que Raku n'est pas entièrement dépourvu d'opinion dans ce domaine. Votre question touche à deux thèmes de la conception de Raku, qui méritent tous deux une petite discussion.
Raku a des valeurs l de première classe
Raku utilise abondamment les valeurs L étant une chose de première classe. Quand on écrit:
La méthode générée est:
L'
is rw
ici indique que la méthode renvoie une valeur l , c'est-à-dire quelque chose qui peut être assigné. Ainsi, lorsque nous écrivons:Ce n'est pas du sucre syntaxique: c'est vraiment un appel de méthode, puis l'opérateur d'affectation appliqué au résultat. Cela fonctionne, car l'appel de méthode renvoie le
Scalar
conteneur de l'attribut, qui peut ensuite être affecté à. On peut utiliser la liaison pour diviser cela en deux étapes, pour voir que ce n'est pas une transformation syntaxique triviale. Par exemple, ceci:Serait l'attribution à l'attribut d'objet. Ce même mécanisme est à l'origine de nombreuses autres fonctionnalités, notamment l'attribution de liste. Par exemple, ceci:
Fonctionne en construisant un
List
contenant les conteneurs$x
et$y
, puis l'opérateur d'affectation dans ce cas itère chaque côté par paire pour effectuer l'affectation. Cela signifie que nous pouvons y utiliser desrw
accesseurs d'objets:Et tout cela fonctionne naturellement. C'est également le mécanisme derrière l'attribution aux tranches de tableaux et de hachages.
On peut également l'utiliser
Proxy
pour créer un conteneur de valeur l où le comportement de lecture et d'écriture est sous votre contrôle. Ainsi, vous pourriez mettre les actions secondaires enSTORE
. Toutefois...Raku encourage les méthodes sémantiques sur les "colons"
Lorsque nous décrivons OO, des termes comme «encapsulation» et «masquage de données» reviennent souvent. L'idée clé ici est que le modèle d'état à l'intérieur de l'objet - c'est-à-dire la façon dont il choisit de représenter les données dont il a besoin pour implémenter ses comportements (les méthodes) - est libre d'évoluer, par exemple pour gérer de nouvelles exigences. Plus l'objet est complexe, plus cela devient libérateur.
Cependant, les getters et setters sont des méthodes qui ont une connexion implicite avec l'État. Bien que nous puissions prétendre que nous réalisons la dissimulation des données parce que nous appelons une méthode, sans accéder directement à l'état, mon expérience est que nous nous retrouvons rapidement à un endroit où le code extérieur effectue des séquences d'appels de setter pour effectuer une opération - ce qui est une forme de la fonctionnalité envie anti-modèle. Et si nous faisons cela , il est assez certain que nous allons finir avec la logique en dehors de l'objet qui fait un mélange d'opérations getter et setter pour réaliser une opération. Vraiment, ces opérations auraient dû être exposées comme des méthodes avec un nom qui décrit ce qui est réalisé. Cela devient encore plus important si nous sommes dans un environnement simultané; un objet bien conçu est souvent assez facile à protéger à la frontière de la méthode.
Cela dit, de nombreuses utilisations de
class
sont vraiment des types d'enregistrement / produit: elles existent pour simplement regrouper un ensemble d'éléments de données. Ce n'est pas un hasard si le.
sceau ne génère pas seulement un accesseur, mais aussi:class Point { has $.x; has $.y; }
peut être instancié commePoint.new(x => 1, y => 2)
), et le rend également dans le.raku
méthode de vidage..Capture
objet par défaut , ce qui signifie que nous pouvons l'utiliser dans la déstructuration (par exemplesub translated(Point (:$x, :$y)) { ... }
).Quelles sont les choses que vous souhaiteriez si vous écriviez dans un style plus procédural ou fonctionnel et utilisiez
class
comme moyen de définir un type d'enregistrement.La conception Raku n'est pas optimisée pour faire des choses intelligentes dans les setters, car cela est considéré comme une mauvaise chose à optimiser. C'est au-delà de ce qui est nécessaire pour un type d'enregistrement; dans certaines langues, nous pourrions dire que nous voulons valider ce qui est attribué, mais dans Raku, nous pouvons nous tourner vers des
subset
types pour cela. En même temps, si nous faisons vraiment une conception OO, nous voulons une API de comportements significatifs qui cache le modèle d'état, plutôt que de penser en termes de getters / setters, ce qui a tendance à conduire à un échec de colocalisation les données et le comportement, ce qui est de toute façon la raison de faire de l'OO.la source
Proxy
s (même si je l'ai suggéré ha). La seule fois où je les ai trouvés terriblement utiles, c'est pour moiLanguageTag
. En interne, le$tag.region
renvoie un objet de typeRegion
(car il est stocké en interne), mais la réalité est, il est infiniment plus pratique pour les gens de dire$tag.region = "JP"
plus$tag.region.code = "JP"
. Et ce n'est vraiment que temporaire jusqu'à ce que je puisse exprimer une contrainteStr
dans le type, par exemple,has Region(Str) $.region is rw
(qui nécessite deux fonctionnalités distinctes planifiées mais de faible priorité)Eh bien, c'est vraiment à l'architecte. Mais sérieusement, non, ce n'est tout simplement pas la façon standard dont Raku fonctionne.
Maintenant, il serait tout à fait possible de créer un
Attribute
trait dans l' espace de module, quelque chose commeis settable
, qui créerait une méthode accesseur alternative qui serait d' accepter une valeur unique pour définir la valeur. Le problème avec cela est, à mon avis, qu'il y a fondamentalement 2 camps dans le monde sur la valeur de retour d'un tel mutateur: cela retournerait-il la nouvelle valeur, ou l' ancienne valeur?Veuillez me contacter si vous êtes intéressé à implémenter un tel trait dans l'espace module.
la source
Je soupçonne actuellement que vous venez de devenir confus. 1 Avant de toucher à cela, commençons par ce qui ne vous dérange pas:
Vous pouvez faire toutes ces choses. C'est-à-dire que vous utilisez l'
=
affectation, les méthodes multiples et "allez-y$c.v( 43 )
", tout en même temps si vous voulez:Une source possible de confusion 1
Dans les coulisses,
has $.foo is rw
génère un attribut et une méthode unique dans le sens de:Ce qui précède n'est pas tout à fait correct cependant. Compte tenu du comportement que nous constatons, la
foo
méthode de génération automatique du compilateur est en quelque sorte déclarée de telle sorte que toute nouvelle méthode du même nom l' ombre en silence . 2Donc, si vous voulez une ou plusieurs méthodes personnalisées portant le même nom qu'un attribut, vous devez répliquer manuellement la méthode générée automatiquement si vous souhaitez conserver le comportement dont elle serait normalement responsable.
Notes de bas de page
1 Voir la réponse de jnthn pour un compte rendu clair, complet et faisant autorité de l'opinion de Raku sur les getters / setters privés vs publics et ce qu'il fait dans les coulisses lorsque vous déclarez des getters / setters publics (c.-à-d. Écrire
has $.foo
).2 Si une méthode d'accesseur générée automatiquement pour un attribut était déclarée
only
, Raku lèverait, je présume, une exception si une méthode portant le même nom était déclarée. Si elle a été déclaréemulti
, elle ne doit pas être masquée si la nouvelle méthode a également été déclaréemulti
et doit lever une exception dans le cas contraire. Ainsi, l'accesseur généré automatiquement est déclaré avec nionly
ni,multi
mais à la place d'une manière qui permet l'observation silencieuse.la source