Estoy descubriendo este nuevo lenguaje y me resulta apasionante en muchas cosas. Tiene una sintaxis violentamente simplificadora -y a veces inentendible- y excepcionales herramientas para comparar strings y asignar variables.
Pero estoy chocando con una simplifación y necesito ayuda… al parecer, en ningún ejemplo que esté mirando encuentro algo que me desmienta: el polimorfismo en Perl5 es un pegote. No sé qué se estará preparando para Perl6, pero la razón fundamental por la que uno va a buscar polimorfismo en un lenguaje está viciada: no se puede escribir una clase que descienda de otra para corregir la original.
Pongamos un ejemplo: En la clase Weather::Google la definición de la URL para conectar con el servicio es una constante. Es un error porque este tipo de propiedades no está escrito en piedra. Si queremos agregarle la propiedad de lenguaje a la dirección -hay que agregarle “hl=es&” para que devuelva los resultados en español- estamos en un problema. Pero aparentemente, Perl viene al rescate dejándonos escribir cosas directamente en la clase, o admitiendo el polimorfismo para que reescribamos un método que nosotros objetamos.
Pues no. Todos los métodos que intenté -incluídos los mamarrachos- me dejan a las claras un comportamiento simple del Perl: todo lo que sobreescriba es como un copy-paste que se pegotea sobre la clase original, pero no la modifica. Veamos este código:
package a; use constant PIE => "This is a PIE\n"; $self->{'myproperty'} = "We are in A\n"; sub new { my $class = shift; my $self = {}; bless ($self,$class); assignProperty(); return $self; } sub assignProperty{ $self->{'myproperty'} = "We are in A, but something changed\n"; }; sub EatMy{ print PIE; } sub printProperty{ print $self->{'myproperty'}; } return 1;
Ahora, la clase que intenta sobreescribir al paquete A:
package b; BEGIN { @ISA = 'a'} ; use constant PIE => "We are in B\n"; $self->{'myproperty'} = "We are in B\n"; sub assignProperty{ $self->{'myproperty'} = "We are in B, changing things\n"; }; 1;
Ahora, veamos que pasa:
use a; use b; $s = new a; $s->EatMy(); $s->printProperty(); $s = new b; $s->EatMy; $s->printProperty();
Ok, el resultado de todo esto siempre es el mismo:
This is a PIE We are in A, but something changed This is a PIE We are in A, but something changed
Nunca podemos cambiar cosas en la clase original. Para que funcione, tendríamos que reescribir el método printProperty, que es lo que hacen todos los ejemplos y que definitivamente no es lo que queremos cuando hablamos de polimorfismo.
Repasemos qué estamos tratando de hacer:
Clase A
Tiene una propiedad y una constante. En el caso de la constante, nunca tuve mucha fe en poder cambiarla, porque es una constante, bien. Pero tiene una propiedad myproperty que podría aflojar un poco. La asignamos al arranque y la cambiamos en el método new para que diga otra cosa usando el método changeproperty.
En la clase B reasignamos el método changeproperty para que diga otra cosa. Ahora creamos un una variable de la clase B. El método new no está en la clase B, por lo que el Perl va a buscarla en la clase A. Pero una vez que lo buscó, las referencias a otros métodos deberían reubicarse. Si existe un método en la clase B que fue cambiado, la ejecución del método new en la clase A debería ir a buscar al método changeporperty de la nueva clase, y sin embargo se queda en la clase A para olvidarse de que la variable que inicializamos es de la clase B.
Es como si el Perl inicializara otra variable de clase A para emparchar lo que le falta a la clase B. Si llamamos expresamente al método changeproperty de la clase B tampoco arreglamos las cosas: Perl tiene una copia de la variable para la clase A y otra para la clase B. La única forma que le estoy encontrando por ahora es hacer que llamar a un método de la clase madre para que asigne la variable de la otra clase. Tengo que ver si puedo acceder a las variables de la clase madre, pero hasta ahora me da error de sintaxis. Si en la clase madre no tengo un método que asigne la variable, por ahora estoy frito.
Por ahora, la arregalaría haciendo esto:
package b; BEGIN { @ISA = 'a'} ; use constant PIE => "We are in B\n"; $self->{'myproperty'} = "We are in B\n"; sub assignProperty{ a::assignProperty("We are in B"); #$self->{'myproperty'}) = "We are in B, changing things\n"; }; 1;
Por supuesto, en el paquete A cambié el método assignProperty para que diga esto
sub assignProperty{ $self->{'myproperty'} = shift || "We are in A, but something changed\n"; };
y pueda tomar por como opción un parámetro y lo asigne… me pregunto si existe un $parent o algo así, en lugar de $self… con eso manoteo la variable de la clase que estoy derivando… probemos. Nop! $parent no existe. Googliemos… nada aparente. Parent no es. Super parece hecho para métodos y no para variables o cosas así… sigo mirando.