Tal vez me falta algo, pero me gustaría saber si hay una buena razón por la cual este código debe compilarse
role L {
method do-l (Int, Int --> Int ) { ... }
}
class A does L {
method do-l (Int $a, Real $b --> Str) {
.Str ~ ": Did you expect Int?" with $a + $b
}
}
my $a = A.new;
say $a.do-l: 2, 3.323
Esto dará salida
5.323: Did you expect Int?
Tenía curiosidad por saber si alguien conoce una forma para que el compilador al menos arroje alguna advertencia con la firma implementada de la función L
.
L.do-l
tiene un método que no coincide, y en cualquier caso, el método de la clase tiene prioridad, por lo que en ambas bases,A
'sdo-l
debe ser llamado. La adición de anInt
y aReal
será aReal
....
(es cierto que, aunque tiene sentido en el código de producción, aquí puede causar confusión)role foo {}.new
que se convierte automáticamente en algo asíanon class foo does role foo {}.new
) es tan transparente que la gente puede no darse cuenta de que es un juego de palabras, de ahí mi comentario anterior. Es bueno que este juego de palabras por sí solo habilite la alternativa principal a la herencia y a la delegación, es decir, organizar y construir tipos usando la composición , pero en realidad es importante que la gente no pierda de vista por completo que.new
es un juego de palabras.Respuestas:
Lo obtienes si prefijas la declaración del método con
multi
:Con esto su programa muestra:
Creo que la intención del diseño era apoyar dos nociones de composición polimórfica:
Sin el
multi
, la aplicación solo se relaciona con la existencia de un método con el nombre correcto; Los parámetros son ignorados.Con el
multi
, la aplicación cubre el nombre y todos los parámetros también ( o algunos ).Mi opinión personal sobre si hay una buena razón para:
¿Apoyando dos sabores del método polimorfismo? A veces es útil hacer cumplir estrictamente la firma. A veces se interpone en el camino.
Distinguirlos a través de
multi
? La aplicación completa de la firma requiere que las clases / roles de implementación tengan un método con exactamente la misma firma. Pero, ¿qué sucede si una clase / rol implementador quiere manejar un parámetro enint
lugar deInt
un parámetro? Raku se compromete. Proporcionado una clase / función de ejecución tiene un método exactamente compatible que puede también tener variaciones. La manera perfecta de transmitir esto es prefijar un método con trozosmulti
.¿Tener el nombre predeterminado solo es polimorfismo? Podríamos haber elegido la
multi
semántica como predeterminada, y hacer que los usuarios escribieran unonly
prefijo si solo quisieran nombrar polimorfismo. Pero eso revertiría la situación habitual (es decir, ignorar los métodos apagados). En términos más generales, la intención es que Raku proporcione una amplia gama de restricciones para sus características, desde relajadas hasta tensas, y elige un valor predeterminado para cualquier característica determinada que se considere correcta en función de los comentarios de los usuarios a lo largo de los años.¿Qué pasa si el valor predeterminado no parece correcto? ¿Qué pasa si el rango existente de estenosis no es suficiente? ¿Qué pasa si un grupo piensa que deberíamos ir a la izquierda y otro piensa que deberíamos ir a la derecha?
Raku tiene (imo) mecanismos de gobierno notables para apoyar la evolución del lenguaje impulsado por el usuario. En el nivel superior hay elementos como la arquitectura de la trenza . En el nivel inferior hay elementos como tipos versionables . En el medio hay elementos como los
RoleToClassApplier
que median el proceso de aplicar un rol a una clase, que es el punto en el que se necesita encontrar un método requerido o la construcción de la clase fallará. En resumen, si el idioma no funciona de la manera deseada, incluidas las restricciones, puede, al menos en principio, cambiarlo para que funcione.fuente
multi
en este trozo no es intuitiva, pero ahora, gracias a su explicación, tiene sentido.Supongo que aquí se pregunta por qué no hay advertencia con respecto al tropezón. De hecho, normalmente se debe implementar un método tropezado, pero eso es todo.
Puede ver cómo los roles se componen en clase aquí en la fuente de Rakudo (
$yada
básicamente significa$is-stubbed
):Entonces puede ver que la lógica es solo para ver si existe un método con el mismo nombre. Definitivamente es posible escribir un módulo que actualice esta lógica aumentando el método apply () o reemplazando directamente la
RoleToClassApplier
clase. Sin embargo, puede ser difícil. Por ejemplo, considere:¿Deberíamos considerar que el método stubbed se implementa correctamente? Pero alguien más podría decirlo más tarde
class C is Letter
y de repente no se implementa. (Por supuesto, podríamos decir que la mejor lógica sería requerir, al menos, el mismo o supertipo, pero eso es más inquietante que lo necesario para lenguajes dinámicos, IMO).No existe, AFAICT, un método que se llama a los roles durante la composición que también hace referencia a la clase (en realidad, no hay ningún método llamado
add_method
), por lo que no hay forma de escribir su propio cheque dentro del rol. (pero podría estar equivocado, tal vez Raiph, Lizmat o Jnthn podrían corregirme).Mi recomendación en este caso sería, en lugar de tropezar, simplemente agregar una declaración de matriz:
Esto no lo capturará en la compilación, pero si en algún momento
L
necesita recurrir a otro método,do-l(Int, Int)
incluso si la clase de composición implementa otras firmas, se llamará y el error se detectará con bastante rapidez.fuente