Raku rebless ya no funciona con clases heredadas

9

El código dado en este hilo ya no funciona: ¿Cómo puedo rebadificar un objeto en Perl 6?

Escribí este código el año pasado, y funcionó entonces. Ahora no lo hace:

class Person { ; }
class Woman is Person { ; }
my $tom = Person.new;
my $lisa = Woman.new;

say $tom.^name;  # -> Person
say $lisa.^name; # -> Woman

Metamodel::Primitives.rebless($tom, Woman);
# -> New type Woman for Person is not a mixin type

El mensaje de error no tiene sentido, ya que se supone que funciona con clases heredadas. Al menos lo fue.

La documentación no es útil; https://docs.raku.org/routine/rebless

Arne Sommer
fuente
Podría ser un error de regresión. Probablemente sea mejor informarlo como un problema de Rakudo.
jjmerelo
Hubo algunos cambios el pasado febrero: github.com/perl6/nqp/blob/…
jjmerelo
Además, he actualizado la documentación con una nota al pie que apunta a @jnthn answer docs.raku.org/type/Metamodel::Primitives . Gracias, raiph
jjmerelo

Respuestas:

11

se supone que funciona con clases heredadas

Nunca se suponía que fuera tan general. Diseñé esa API y la implementé en primer lugar, y solo pretendía ser un detalle de implementación de mixins.

Hasta hace muy poco, no formaba parte del conjunto de pruebas de especificación de lenguaje, y cuando se convirtió en parte de él, ya tenía su semántica actual, más restrictiva. Las restricciones son importantes por razones de rendimiento: cuando sabemos que un tipo no es uno que puede ser el objetivo de una operación mixin, podemos compilar JIT los accesos de atributos en ese objeto en algo mucho más simple (pagamos un movimiento condicional adicional en cada atributo accede antes del cambio, y ahora solo tiene que pagarlo en los tipos de destino mixin).

Es posible modificar el programa original para que funcione utilizando el MOP para construir la clase. De hecho, el siguiente no es el programa original; Hice un pequeño ajuste con el fin de mostrar cómo se pueden proporcionar métodos en la subclase como un rol anónimo, para evitar demasiado repetitivo MOP.

class Person { method m() { "person" } }
constant Woman = do {
    my \w = Metamodel::ClassHOW.new_type(:is_mixin, :name<Woman>);
    w.^add_parent(Person);
    w.^add_role(role { method m() { "woman" } });
    w.^compose()
}
my $tom = Person.new;
my $lisa = Woman.new;

say $tom.^name;  # -> Person
say $lisa.^name; # -> Woman

say $tom.m; # person
Metamodel::Primitives.rebless($tom, Woman);
say $tom.m; # woman

Si bien esa es la solución semánticamente directa al programa original, hay una forma más corta: use el butoperador en el Personobjeto de tipo para producir un tipo de mezcla y devolverlo, y luego simplemente modifique su nombre a su gusto:

class Person { method m() { "person" } }
constant Woman = Person but role { method m() { "woman" } }
BEGIN Woman.^set_name('Woman');

my $tom = Person.new;
my $lisa = Woman.new;

say $tom.^name;  # -> Person
say $lisa.^name; # -> Woman

say $tom.m;
Metamodel::Primitives.rebless($tom, Woman);
say $tom.m;

Que es solo una línea extra que la original de todos modos.

Jonathan Worthington
fuente
constant Woman = Person but role …No me di cuenta de que eso se podía hacer. ¡Y por lo tanto, pero para la BEGINlínea, Raku casi logra ser capaz de hacer un paradigma prototípico de estilo JS también!
user0721090601
Okay. Gracias por la explicación. Espero que se encuentre en la documentación, ya que docs.raku.org/routine/rebless es bastante inútil ... Actualizaré «Beginning Raku» en breve.
Arne Sommer el
@ user0721090601 Raku admite, citando S12: "programación OO basada tanto en clase como en prototipo" . Sin embargo, si construye objetos usando la classpalabra clave , citando nuevamente S12: "por defecto, los objetos derivados de Muun modelo basado en clases bastante estándar ... bless... llama ... rutinas BUILD ... la semántica BUILD predeterminada es heredado de Mu". En resumen, diría que es más exacto decir que Raku admite A) "deformar seriamente incluso el OO basado en clase estándar con solo un par de líneas de código" y B) "OO basado en prototipo".
Raiph
Visite raku-musings.com/reblessed.html para ver mi opinión sobre los cambios importantes.
Arne Sommer
5

Vea la respuesta de jnthn para una discusión autorizada sobre exactamente qué sucedió reblessy qué hacer al respecto.

funcionó ... Ahora no funciona ... El mensaje de error no tiene sentido ... se supone que funciona con clases heredadas ... Al menos fue ... La documentación no es útil

Puede valer la pena leer esta respuesta (¡muy larga!) Para aquellos interesados ​​en una discusión más profunda de los principios y la práctica del enfoque TDD que subyace al trabajo en el lenguaje de programación Raku y los artefactos relacionados, como el compilador Rakudo y el contenido docs.raku.org .

Esta respuesta está estructurada como respuestas específicas a partes particulares de la pregunta original de Arne y de los comentarios que escribieron en respuesta a una versión anterior de esta respuesta. Mi intención era hacerlo más útil para Arne mientras, con suerte, seguir siendo útil para los demás.

Arne: El código dado en este hilo ya no funciona: ¿cómo puedo rebadificar un objeto en Raku?

He actualizado la respuesta aceptada a ese SO para vincular a este SO.

Arne: Escribí este código el año pasado, y funcionó entonces. Ahora no

El cambio relevante se discutió en una confirmación de abril de 2019 en la que jnthn escribió:

Recientemente, los tipos que fueron el objetivo de una reblessoperación comenzaron a necesitar crearse explícitamente como tipos de objetivos mixin, para ayudar a la optimización. ...

En un comentario hace 11 días cerrando el problema rakudo GH "Rebless a un tipo personalizado ya no parece funcionar" , escribió:

Tendrá que hacer arreglos para que se is_mixinpase el argumento nombrado a ClassHOW.new_type... No hay forma de hacerlo con la sintaxis de clase, por lo tanto, el tipo de destino de la rebless tendrá que ensamblarse también usando el MOP.

(Haga clic en el enlace de arriba para obtener notas sobre cómo hacer lo que sugiere).

Este problema también se discute un poco más en el sentido de que funcionó ... de repente no ... la documentación ... debe documentar la sección de llamadas a continuación.

Arne: se supone que funciona con clases heredadas. Al menos lo fue.

asado - el r epository o f una ll s pec t EST - determina lo que se supone código Raku hacer. (El st de roa st puede leerse como s upposed t o s.)

En otro mensaje de abril de 2019, jnthn escribió:

No había especificación previa para Metamodel::Primitives.rebless. He agregado esta especificación para que ahora la haya. Esto significa que ahora hay una definición de lo que se puede esperar que funcione.

El hecho de que el comportamiento de Rakudo sea especificado por un conjunto de pruebas ejecutables es una parte fundamental del enfoque de @Larry para garantizar que Raku se comporte de manera confiable [1] y tenga profundas implicaciones [2] .

El impacto de este cambio en un módulo ampliamente utilizado

Aquí hay una instantánea del impacto de este cambio que se desarrolla para el popular módulo Inline :: Perl5.

En abril de 2019, niner abrió un problema rakudo GH sobre el impactoInline::Perl5 y he extraído algunos aspectos destacados del intercambio entre niner y jnthn a continuación.

(He eludido algunas cosas que eran importantes en el contexto original, pero que distraen en el contexto de este SO. No suponga que comprende completamente la conversación original de este extracto. En caso de duda, haga clic en el enlace. )

Niner: TBH, lo que hago aquí probablemente siempre ha sido un poco sospechoso ... Incluso podría ser eso ... Puedo deshacerme de [eso] ... Sin embargo, sería bueno mantener ya implementadas las versiones Inline :: Perl5 en funcionamiento .

jnthn: No había especificación previa para Metamodel::Primitives.rebless. He agregado [a] spectest para que ahora haya. Esto significa que ahora hay alguna definición de lo que se espera que funcione, y en qué Inline :: Perl5 puede confiar.

Dado que los parámetros con nombre desconocido se ignoran, pero :mixinno se requerían en versiones anteriores de Rakudo, entonces sería posible hacer una nueva versión de Inline :: Perl5 que pueda funcionar en versiones anteriores de Rakudo, así como en la próxima, por lo que al menos puede haber retrocompatible

No creo que haya ninguna manera de mantener las cosas funcionando para las versiones existentes de Inline :: Perl5 ...

niner: Desafortunadamente, pasar :mixinno ayuda en este caso, ya que el rebless se realiza en una subclase de la creada a través de Metamodel::Primitives.create_type. La subclase usa lo normal Perl6::ClassHOW.

Estoy trabajando en un refactor importante para deshacerme del truco sin rebote en primer lugar. Estoy reabriendo este problema para que el administrador de versiones sepa que no hay Inline :: Perl5 que funcione en el candidato de lanzamiento de rakudo.

jnthn: ¿ Creas esa clase usando el MOP? Puede pasar :is_mixina Perl6::ClassHOW.new_typesi es así.

Niner: No, es para esta situación:class Bar is Foo { }

Ayudando con los documentos

En un comentario debajo de esta respuesta que has escrito:

Puedo ayudar con la parte de documentación

Eso me parece una respuesta muy apropiada y útil al problema en el corazón de su SOQ. Espero que tengamos la suerte de que esto suceda.

si eso ayuda

Imo, su redacción técnica es excelente, así que espero que el resultado final de su trabajo con otras personas involucradas en mejorarlo sea algo maravilloso.

Restricciones fundamentales sobre el contenido de docs.raku.org

Una gran parte de la razón por la que escribí el resto de esta respuesta tan extensa a una pregunta aparentemente tan simple, y la restablecí después de eliminarla inicialmente una vez que Jonathan la había respondido, fue para discutir los principios y la práctica del enfoque TDD que subyace en el trabajo el lenguaje de programación Raku y los artefactos relacionados, como el compilador Rakudo y el contenido docs.raku.org .

Aiui, la relación deseable entre cómo se supone que funcionan las cosas en Raku, y cómo funcionan realmente en Rakudo, y cómo se supone que las cosas deben documentarse en docs.raku.org se reduce a:

  • Se DEBE presumir que todo está sujeto para siempre a la naturaleza fundamental de un proyecto voluntario; y, dentro de esa restricción:

  • El comportamiento en el asado DEBE estar documentado y el otro comportamiento NO DEBE.

(Dado el tiempo voluntario disponible, el interés y el consenso, ocasionalmente se hacen excepciones para documentar el comportamiento de un Rakudo de control de calidad adecuado que no está cubierto por el asado. En la práctica actual, esto parece significar el comportamiento de una versión de Rakudo en un Rakudo Star lanzado).

Documentación inútil

La documentación no es útil.

Lo consideré un comentario justo. A fin de cuentas, la documentación tal como estaba cuando escribió su pregunta no fue útil.

la documentación fue inútil [en 2018]

Esta es una afirmación muy diferente.

No había entrada de asado cubriendo reblessen ese momento.

Si la página docs.raku.org rebless hubiera descrito su comportamiento tal como era en 2018, entonces eso hubiera sido peor que inútil porque sugeriría incorrectamente que el comportamiento actual era compatible. En realidad, había margen para que se rompiera en una versión futura de Rakudo sin una perspectiva razonable de que el comportamiento de 2018 sería restablecido por los desarrolladores principales. Y, de hecho, esto sucedió: su comportamiento no compatible a partir de 2018 se rompió y no fue restablecido.

Entonces, dado el consenso sobre lo que pertenece en docs.raku.org y lo que no (ver arriba), lo más útil que reblesspodría hacer su página era no documentar reblessen absoluto o, quizás mejor, incluir una página para ello, pero asegúrese de que no describió su comportamiento. Cuál era la situación: la página existía; no fue directamente útil; y eso fue posiblemente mejor que nada.

(Es fácil imaginar que las cosas mejoren aún. Por ejemplo, ¿qué pasaría si las funciones de documentación de páginas incluyeran un porcentaje que documentara el estado de la cobertura de prueba asociada con esa función en la versión de Rakudo en la última Rakudo Star? Un 0% podría dar pistas de inmediato a un lector en una conciencia de que esa función no estaba cubierta por el asado. Dicho esto, si bien esta función de documentación es fácil de imaginar , ¿quién la implementará? Es igualmente fácil imaginar que podría llevar un año calendario o más de trabajo diligente y colaboración para implementar e implementar de manera útil, y que la gente piense que otras cosas son más importantes).

funcionó ... de repente no ... la documentación ... debería documentar la llamada

funcionó

Fue "suerte" que funcionó.

de repente ya no funcionó

Porque Rakudo fue mejorado.

la documentación ... debe documentar la llamada

Como se explicó anteriormente, todo el consenso de la comunidad actual y / o práctica de trabajo es: la documentación DEBE documentar una versión particular de la llamada, a saber, el comportamiento de tostado para la versión de Rakudo en la última Rakudo Star; y PUEDE documentar el comportamiento en otras versiones.

y no se refieren a otra cosa

Aiui, el consenso actual y / o práctica de trabajo es que lo que algunos podrían considerar contribuciones de documentos "débiles", por ejemplo, algún contenido breve y escrito apresuradamente y / o enlaces fuera de los documentos, PUEDE introducirse si los voluntarios sienten que se justifica un cambio inmediato para reflejar alguna preocupación planteada por un usuario (por ejemplo, este SO) y que hacer el cambio "débil" sería mejor que no hacer nada en absoluto. Por supuesto, puede hacer un RP para mejorarlo (o revertirlo si realmente siente que un cambio es tan "débil" que empeora las cosas).

la referencia a los cambios en 2019.11 es de 7 meses de descuento por mi cuenta

(También es algo así por mi cuenta, aunque he visto un compilador que dice ser 2019.03.1 con la misma interrupción en el comportamiento. [3] )

Creo que JJ hizo el cambio de documento y simplemente malinterpretó el comentario de Jnthn sobre cómo adaptarse al cambio. Actualmente creo que es mejor que nada, pero espero que lo actualices. :)

Notas al pie

[1] Lo siguiente se dijo unos minutos después de que Larry anunciara por primera vez el proyecto que llevó a Raku en su discurso de 2000 "El estado de la cebolla" :

Pregunta: ¿Tendrá [Raku] especificaciones?

Larry: lo que queremos enfatizar particularmente ... quizás no sea tanto la especificación [del diseño del lenguaje] como el desarrollo de nuestra prueba de regresión actual ... en una prueba de validación de lo que realmente significa el lenguaje y realmente salir y explorar todos los rincones y grietas y dicen: "Esto es [Raku], esto no es [Raku]", y luego tenemos una especificación legible por máquina. Y para mí eso es realmente mucho más importante de lo que dice la palabrería en lo legible para los humanos.

[2] Por supuesto, el asado solo funciona bien para un usuario dado si sus pruebas cubren suficientemente las necesidades del usuario. El problema de Arne demuestra cómo los agujeros en la cobertura pueden ser sorprendentes. Para una discusión sobre estos agujeros tal como estaban en 2018, consulte Especificaciones, versiones, cambios y ... Rotura . La buena noticia es que el asado es solo un montón de pruebas unitarias escritas en Raku para probar que las expresiones o construcciones con valores particulares hacen algo en particular. Por lo tanto, es fácil para las personas o empresas contribuir con nuevas pruebas para mejorar la cobertura de las pruebas. Y todo está bajo control de versiones (git), por lo que las etiquetas, ramas y horquillas posteriores personalizadas son viables, sostenibles y manejables. (De hecho, eso es cómo las nuevas versiones de idioma ( Christmas, Diwali, Eid(?), Etc.) se gestionan.)

[3] He visto un intento de rechazar una nueva clase creada usando la newclass is oldclasssintaxis regular, tanto funciona (en mi computadora portátil) como no funciona (en repl.it) usando compiladores que dicen ser 2019.03.1. (Presumbly repl.it instaló una versión del código fuente del compilador, o un binario compilado a partir de él, tomado de la cabeza maestra poco después de actualizar la versión del compilador 2019.03.1, con el cambio de ruptura en su lugar. Noto que repl.it haven ' No publiqué su respuesta de raku en línea, lo descubrí por accidente, por lo que no hay nada desagradable en esta situación, pero me reforzó la necesidad del $RAKU.compiler.verbose-configmétodo utilizado en los resultados trabajados / rotos que acabo de vincular.

raiph
fuente
Encontré este artículo cuando traté de descubrir cómo funciona "rebless", ya que la documentación era inútil: stackoverflow.com/questions/44486985/... Y funcionó, entonces. Y de repente ya no funcionó, y la documentación seguía siendo inútil. Todavía lo es, ya que debería documentar la llamada y no referirse a otra cosa. Y la referencia a los cambios en 2019.11 tiene 7 meses de descuento según mi recuento.
Arne Sommer
Puedo ayudar con la parte de documentación, si eso ayuda.
Arne Sommer
@ArneSommer Consulte las nuevas secciones en mi respuesta comenzando con Ayuda con los documentos .
Raiph