¿Es esta la intención del diseño de Raku?
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:
has $.x is rw;
El método que se genera es:
method x() is rw { $!x }
El is rw
aquí indica que el método devuelve un valor L - es decir, algo que se puede asignar a. Así cuando escribimos:
$obj.x = 42;
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:
my $target := $obj.x;
$target = 42;
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:
($x, $y) = "foo", "bar";
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 usar rw
accesores de objetos allí:
($obj.x, $obj.y) = "foo", "bar";
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 en STORE
. 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:
- Opta que el atributo se establezca mediante la lógica de inicialización de objeto predeterminada (es decir,
class Point { has $.x; has $.y; }
se puede instanciar como Point.new(x => 1, y => 2)
), y también lo representa en el .raku
método de volcado.
- Opta por el atributo en el
.Capture
objeto predeterminado , lo que significa que podemos usarlo en la desestructuración (p sub 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.