#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)
hasta ahora, tan razonable, y luego
#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
Me gusta la inmediatez de la asignación '=', pero necesito la facilidad de bunging en acciones paralelas que proporcionan los métodos múltiples. Entiendo que estos son dos mundos diferentes, y que no se mezclan.
PERO - No entiendo por qué no puedo ir solo $ cv (43) Para establecer un atributo público
- Siento que el raku me está guiando para no mezclar estos dos modos, algunos atributos privados y otros públicos, y que la presión es hacia el método del método (con algunos: azúcar del colon), ¿es esta la intención del diseño de Raku?
- ¿Me estoy perdiendo de algo?
is rw
descriptor de acceso ya devuelve un contenedor cuando se especifica. Devolver un Proxy no va a cambiar el número de parámetros permitidos en el descriptor de acceso.= foo
y.(foo)
para configurar) y permitir que se hagan efectos secundarios en ambos casos (pero no solo cuando se obtienenRespuestas:
Es justo decir que Raku no es del todo indiscutible en esta área. Su pregunta toca dos temas en el diseño de Raku, que vale la pena discutir un poco.
Raku tiene valores l de primera clase
Raku hace un uso abundante de los valores l como algo de primera clase. Cuando escribimos:
El método que se genera es:
El
is rw
aquí indica que el método devuelve un valor L - es decir, algo que se puede asignar a. Así cuando escribimos:Esto no es azúcar sintáctica: realmente es una llamada a un método, y luego el operador de asignación se aplica al resultado de la misma. Esto funciona porque la llamada al método devuelve el
Scalar
contenedor del atributo, que luego se puede asignar. Se puede usar el enlace para dividir esto en dos pasos, para ver que no es una transformación sintáctica trivial. Por ejemplo, esto:Estaría asignando al atributo del objeto. Este mismo mecanismo está detrás de muchas otras características, incluida la asignación de listas. Por ejemplo, esto:
Funciona mediante la construcción de un
List
contenedor que contiene los contenedores$x
y$y
, a continuación, el operador de asignación en este caso itera cada lado por pares para hacer la asignación. Esto significa que podemos usarrw
accesores de objetos allí:Y todo funciona naturalmente. Este es también el mecanismo detrás de la asignación a sectores de matrices y hashes.
También se puede usar
Proxy
para crear un contenedor de valor l donde el comportamiento de leerlo y escribirlo esté bajo su control. Por lo tanto, podría poner las acciones secundarias enSTORE
. Sin embargo...Raku fomenta los métodos semánticos sobre los "setters"
Cuando describimos OO, a menudo aparecen términos como "encapsulación" y "ocultación de datos". La idea clave aquí es que el modelo de estado dentro del objeto, es decir, la forma en que elige representar los datos que necesita para implementar sus comportamientos (los métodos), es libre de evolucionar, por ejemplo, para manejar nuevos requisitos. Cuanto más complejo es el objeto, más liberador se vuelve.
Sin embargo, getters y setters son métodos que tienen una conexión implícita con el estado. Si bien podríamos afirmar que estamos logrando ocultar datos porque estamos llamando a un método, no accediendo al estado directamente, mi experiencia es que rápidamente terminamos en un lugar donde el código externo está haciendo secuencias de llamadas de setter para lograr una operación, que es una forma de la envidia característica antipatrón. Y si estamos haciendo eso , es bastante seguro que terminaremos con una lógica fuera del objeto que hace una combinación de operaciones getter y setter para lograr una operación. Realmente, estas operaciones deberían haberse expuesto como métodos con nombres que describen lo que se está logrando. Esto se vuelve aún más importante si estamos en un entorno concurrente; un objeto bien diseñado a menudo es bastante fácil de proteger en el límite del método.
Dicho esto, muchos usos de
class
son realmente tipos de registros / productos: existen para agrupar simplemente un grupo de elementos de datos. No es casualidad que el.
sigilo no solo genere un accesorio, sino también:class Point { has $.x; has $.y; }
se puede instanciar comoPoint.new(x => 1, y => 2)
), y también lo representa en el.raku
método de volcado..Capture
objeto predeterminado , lo que significa que podemos usarlo en la desestructuración (psub translated(Point (:$x, :$y)) { ... }
. Ej .).¿Cuáles son las cosas que desearía si estuviera escribiendo en un estilo más procesal o funcional y utilizando
class
como un medio para definir un tipo de registro?El diseño de Raku no está optimizado para hacer cosas inteligentes en setters, porque eso se considera algo pobre para optimizar. Está más allá de lo que se necesita para un tipo de registro; en algunos idiomas podríamos argumentar que queremos validar lo que se está asignando, pero en Raku podemos recurrir a los
subset
tipos para eso. Al mismo tiempo, si realmente estamos haciendo un diseño OO, entonces queremos una API de comportamientos significativos que oculte el modelo de estado, en lugar de pensar en términos de captadores / establecedores, que tienden a provocar una falla en la colocación. datos y comportamiento, que de todos modos es el punto de hacer OO.fuente
Proxy
s (aunque lo sugerí ha). La única vez que los he encontrado terriblemente útiles es para miLanguageTag
. Internamente, la$tag.region
devuelve un objeto de tipoRegion
(como se encuentra almacenada internamente), pero la realidad es, que es infinitamente más conveniente que la gente diga$tag.region = "JP"
sobre$tag.region.code = "JP"
. Y eso es realmente solo temporal hasta que pueda expresar una coerciónStr
en el tipo, por ejemplo,has Region(Str) $.region is rw
(que requiere dos características planificadas pero de baja prioridad separadas)Bueno, eso depende realmente del arquitecto. Pero en serio, no, esa no es la forma estándar en que trabaja Raku.
Ahora, sería perfectamente posible crear un
Attribute
rasgo en el espacio módulo, algo asíis settable
, que crearía un método de acceso alternativo que podría aceptar un único valor para ajustar el valor. El problema con hacer esto en el núcleo es que creo que hay básicamente 2 campos en el mundo sobre el valor de retorno de un mutador: ¿devolvería el nuevo valor o el antiguo? valor?Contácteme si está interesado en implementar tal característica en el espacio del módulo.
fuente
Actualmente sospecho que te has confundido. 1 Antes de tocar eso, comencemos con lo que no te confunde:
Usted puede hacer todas estas cosas. Es decir, utiliza la
=
asignación y los métodos múltiples, y "simplemente vaya$c.v( 43 )
", todo al mismo tiempo si desea:Una posible fuente de confusión 1
Detrás de escena,
has $.foo is rw
genera un atributo y un método único en la línea de:Sin embargo, lo anterior no es del todo correcto. Dado el comportamiento que estamos viendo, autogenerado del compilador de
foo
alguna manera se está declarando método de tal manera que cualquier nuevo método con el mismo nombre en silencio sombras de TI. 2Entonces, si desea uno o más métodos personalizados con el mismo nombre que un atributo, debe replicar manualmente el método generado automáticamente si desea conservar el comportamiento del que normalmente sería responsable.
Notas al pie
1 Vea la respuesta de jnthn para una descripción clara, exhaustiva y autorizada de la opinión de Raku sobre captadores / creadores privados y públicos y lo que hace detrás de escena cuando declara captadores / setters públicos (es decir, escribir
has $.foo
).2 Si se declarara un método de acceso autogenerado para un atributo
only
, entonces supongo que Raku lanzaría una excepción si se declarara un método con el mismo nombre. Si se declararamulti
, entonces no debería ser sombreado si el nuevo método también fue declaradomulti
, y debería lanzar una excepción si no. Por lo tanto, el descriptor de acceso autogenerado se declara con nionly
ni con,multi
sino que de alguna manera permite el sombreado silencioso.fuente