[caracas-pm] [P] Sobre pragma attributes, su funcionamiento y utilidad

RrodolfoS . rrodolfos at gmail.com
Mon Jan 11 05:53:22 PST 2010


Ernesto,

Yo recibí el mensaje de la lista de pm.org el día 27 de diciembre de
2009, supongo que a los demás también le llegó excepto a ti.

De hecho me fue muy útil, gracias.

Saludos,

Rrodolfo Sauce-Guinand

2010/1/10 Ernesto Hernández-Novich <emhnemhn at gmail.com>:
> Originalmente envié esta respuesta el 2009-12-27, pero aparentemente no llegó
> a pesar de que mis bitácoras confirman que el mensaje llegó hasta pm.org
>
> On Sat, 2009-12-19 at 11:31 +0000, RrodolfoS . wrote:
>> Buenos días a todos,
>>
>> En lo últimos días he estado revisando Catalyst y realizando una pausa
>> con cada duda que se me presenta al recorrer el tutorial, no logro
>> entender el funcionamiento de los atributos en la definición de las
>> acciones por cada controlador, espero ser claro, la duda es con
>> relación al pragma attributes, no encuentro documentación clara al
>> respecto, como funciona, de que forma se usan esos atributos, según
>> (http://perldoc.perl.org/attributes.html), le cambia la semántica al
>> método o a la variable, pero no logro crear un uso práctico, en
>> definitiva no entiendo el citado pragma, si alguien puede dar un
>> ejemplo, o remitirme a alguna documentación mas sencilla se lo
>> agradezco.
>
> Originalmente, sólo había tres tipos de atributos para subrutinas en
> Perl: method, lvalue y locked.
>
> El atributo 'lvalue' permite que una función retorne una dirección
> asignable (l-value) en lugar de un valor (r-value). En una asignación,
> los elementos de la izquierda (l-values) interesan por su ubicación
> ("guardar aquí") mientras que los elementos de la derecha (r-values)
> interesan por su valor ("guardar esto"). Las subrutinas siempre son
> r-values a menos que se utilice el atributo lvalue, i.e.
>
> use feature qw(say)
> my @arreglo;
> sub posicion : lvalue {
>  my $p = shift;
>  $arreglo[ $p ]     # Se retorna la ubicación y no el valor.
> }
> for (0..10) {
>  posicion( $_ ) = $_;
> }
> say $_ foreach (@arreglo);
>
> El atributo 'locked' es importante si el programa es multihilo, pues
> garantiza que uno y sólo un hilo puede ingresar a la subrutina al mismo
> tiempo (lo convierte en una "región crítica"). Si además tiene el
> atributo 'method', se establecerá un bloqueo sobre el primer argumento
> pasado a la función (que usualmente representa el objeto sobre el cual
> se trabaja y que no quisieras sea accedido concurrentemente).
>
> Partiendo de esta idea, se generalizó el concepto de atributos sobre
> subrutinas de manera que el programador pueda poner cualquier cantidad
> de atributos separados por comas. Un atributo es _cualquier_
> identificador (letras, dígitos y guión bajo), y puede tener una lista de
> cosas entre paréntesis (el compilador verifica que los paréntesis estén
> balanceados). Es importante notar que Perl no realiza actividades de
> análisis sintáctico ni semántico sobre el atributo, sino que lo toma
> "tal cual" y lo inyecta en el módulo siguiendo una estrategia simple.
>
> Supongamos que declaras
>
> package Grok;
> use attributes;
> ...
> sub foo : bar(baz,qux), bleh { ... }
>
> luego, en _cualquier_ rutina dentro del package Grok puedes ejecutar
>
> my @attlist = attributes::get( \&foo )
>
> y tendrás @attlist = qw( bar(baz,qux) bleh ). Qué hacer con esa
> información es problema del programador, incluyendo el análisis de cada
> elemento de la lista y su posible "aplicación".
>
> Para simplificar estas tareas existe el módulo Attribute::Handlers, que
> para el mismo ejemplo artificial se usaría
>
> package Grok;
> use Attribute::Handlers;
> no warnings 'redefine';
> sub foo : bar(baz,qux), bleh { ... }
>
> pero le puedes agregar
>
> sub bar : ATTR(CODE) {
>  # Esta función se ejecutará durante la _compilación_ para
>  # _cada_ función que tenga atributo 'bar'. En este ejemplo,
>  # se ejecutará cuando se compila 'foo'.
>  my ($package,$sym,$coderef,$codename,$data,$phase,$file,$line) = @_;
>  ...
> }
>
> Al compilar 'foo' en nuestro ejemplo, se invocaría 'bar' recibiendo:
>
> $package = 'Grok'
> $sym = *foo  (el typeglob)
> $coderef = \&foo (apuntador al código de la rutina)
> $codename = 'foo'
> $data = referencia a un arreglo que tiene 'baz' y 'qux' como elementos
> $phase de la compilación (BEGIN, CHECK, INIT o END).
> Nombre ($file) y línea ($line) del archivo donde está la rutina.
>
> Entonces, todo lo que resta es que el programador de la clase decida qué
> hay que hacer, a tiempo de compilación, con la rutina a la cual se le
> puso algún atributo. Entre la cantidad de cosas que se pueden hacer,
> está analizar los atributos, _generar_ una nueva función que "envuelva"
> al coderef de la función original, y reinstalarla en el nombre original
> (con el mismo estilo de lo que hace Memoize). Por ejemplo, imagina un
> atributo 'Logger' que debe "hacer que cada vez que se ingrese a la
> rutina se escriba un mensaje en una bitácora", entonces dentro del
> manejador podría escribir algo como
>
> sub Logger : ATTR(CODE) {
>  my ($package,$sym,$coderef,$codename,$data,$phase,$file,$line) = @_;
>  # Verificaciones y quien sabe qué otra cosa ...
>  my $newfunc = sub {
>    log_somewhere("Ingresando $codename");
>    $coderef->( @_ );  # La invocación "original"
>    log_somewhere("Terminando $codename");
>  }
>  ${$sym}{CODE} = $newfunc;
> }
>
> Muy resumido: construyo una función anónima que hace el log, invoca a la
> función original pasando los argumentos que se pasarían a la función
> cuando fuese invocada y vuelve a hacer el log. Luego, tomo esa función y
> la instalo en la tabla de símbolos original para que cuando llamen a la
> función original, en realidad llamen a mi función "envoltorio" de la
> función original.
>
> Catalyst saca provecho de esto de manera similar: cuando le pones
> atributos a los métodos de _tu_ clase, a tiempo de compilación son
> manipuladas por los métodos provistos por Catalyst para envolverlas en
> funciones adecuadas. Esas funciones envoltorio están parametrizadas de
> acuerdo a las cosas que permite Catalyst en los atributos.
>
> Entonces, los atributos para subrutinas son _cadenas_, que han de ser
> procesadas a tiempo de _compilación_ por rutinas escritas por el
> _programador_ con el propósito de alterar "algo" en las funciones
> originales. Otra manera de verlo
>
> sub original : alterala(con,estos,parámetros) { ... }
>
> quiere decir que al compilar 'original' habrá que invocar a la rutina
> 'alterala' para que "haga alguna alteración" sobre el código de
> 'original' usando la lista de parámetros ('con', 'estos', 'parámetros').
> ¿Cuál alteración? La que esté implantada en 'alterala' y que puede sacar
> provecho de toda la flexibilidad de typeglobs, coderefs, clausuras y
> modificación dinámica de código (como usar B::Deparse y otras hierbas).
>
> Si, no es fácil de entender a la primera.
>
> --
> Ernesto Hernández-Novich - Linux 2.6.28 i686 - Unix: Live free or die!
> Geek by nature, Linux by choice, Debian of course.
> If you can't aptitude it, it isn't useful or doesn't exist.
> GPG Key Fingerprint = 438C 49A2 A8C7 E7D7 1500 C507 96D6 A3D6 2F4C 85E3
>
>
> _______________________________________________
> caracas-pm mailing list
> caracas-pm at pm.org
> http://mail.pm.org/mailman/listinfo/caracas-pm


More information about the caracas-pm mailing list