Restricción de firma en roles en raku

8

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.

margolari
fuente
2
¿Cuál es su salida esperada? Lo que has mostrado es lo que esperaría. El L.do-ltiene 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's do-ldebe ser llamado. La adición de an Inty a Realserá a Real.
user0721090601
1
Ah, ya veo. En realidad, está preguntando sobre el operador yada ...(es cierto que, aunque tiene sentido en el código de producción, aquí puede causar confusión)
user0721090601
No puede crear instancias de roles si un método está tropezado, pero no hay ningún problema en crear instancias de clases. `clase L {método yadda {...}}; say L.new.raku`
jjmerelo
2
"No se pueden crear instancias de roles si se tropea un método" Los lectores pueden malinterpretar este comentario. No puedes instanciar roles, punto. Son solo fragmentos de una clase. Si intenta crear una instancia de un rol, raku supone que quiere crear una instancia de una clase vacía que cumple ese rol y lo hace por usted. Naturalmente, esto falla si el rol contiene un método tropezado, porque la aplicación es el punto y el significado de un método tropezado en un rol. Por el contrario, tropezar en una clase solo significa "Todavía no he escrito este código".
raiph
2
@jjmerelo Sí, es un juego de palabras de tipo ("subvierte o elude el sistema de tipos"). El que estamos hablando aquí ( role foo {}.newque 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 .newes un juego de palabras.
raiph

Respuestas:

6

lanzar alguna advertencia con la firma implementada de la función L.

Lo obtienes si prefijas la declaración del método con multi:

role L {
  multi method do-l (Int, Int --> Int ) { ... }
}

Con esto su programa muestra:

===SORRY!=== Error while compiling ...
Multi method 'do-l' with signature :(A: Int $, Int $, *%_ --> Int)
must be implemented by A because it is required by a role ...

Me gustaría saber si hay una buena razón por la cual este código debe compilarse [sin el multi]

Creo que la intención del diseño era apoyar dos nociones de composición polimórfica:

  • Sin elmulti , la aplicación solo se relaciona con la existencia de un método con el nombre correcto; Los parámetros son ignorados.

  • Con elmulti , 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 en intlugar de Intun 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 trozos multi.

  • ¿Tener el nombre predeterminado solo es polimorfismo? Podríamos haber elegido la multisemántica como predeterminada, y hacer que los usuarios escribieran un onlyprefijo 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 RoleToClassApplierque 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.

raiph
fuente
1
Wow, esta es exactamente la información que estaba buscando, ojalá hubiera leído sobre esto en los documentos de raku para roles, o tal vez me perdí, si esto no está allí seguramente ayudaría a muchas personas a escribir roles concisos, ya que al principio La vista que tiene que poner multien este trozo no es intuitiva, pero ahora, gracias a su explicación, tiene sentido.
margolari
1
@margolari Es bueno saber que obtuviste la información que necesitabas. Ver también github.com/Raku/doc/issues/3330
raiph
2

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 ( $yadabásicamente significa $is-stubbed):

if $yada {
    unless has_method($!target, $name, 0)
            || $!target.HOW.has_public_attribute($!target, $name) {
        my @needed;
        for @!roles {
            for nqp::hllize($_.HOW.method_table($_)) -> $m {
                if $m.key eq $name {
                    nqp::push(@needed, $_.HOW.name($_));
                }
            }
        }
        nqp::push(@stubs, nqp::hash('name', $name, 'needed', @needed, 'target', $!target));
    }
}

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 RoleToClassApplierclase. Sin embargo, puede ser difícil. Por ejemplo, considere:

class Letter { }
class A is Letter { } 
class B is Letter { }

role Foo {
  method foo (Letter) { ... }
}

class Bar does Foo { 
  method foo (A) { 'a' }
  method foo (B) { 'b' }
}

¿Deberíamos considerar que el método stubbed se implementa correctamente? Pero alguien más podría decirlo más tarde class C is Lettery 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:

role L {
  method do-l(Int $a, Int $b --> Int) {
    die "SORRY! Classes implementing role L must support the signature (Int, Int)";
  }
}

Esto no lo capturará en la compilación, pero si en algún momento Lnecesita 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.

usuario0721090601
fuente
Err, no pensé en que el multi hiciera una gran diferencia cuando raiph descubrió que debería actualizar la respuesta a la luz de eso
user0721090601