2012-05-10 14 views
5

Con Moose, que puede tener lazybuilders en atributos, donde se llama al constructor cuando el atributo es el primer acceder si el atributo no estaba ya poblada. Puede tener la coerción de tipo de un atributo con coerce, pero esto se aplica cada vez que se establece el atributo, por lo que incluso en la inicialización de objetos.Lazy Atributo coerción

Estoy buscando una forma de implementar coerción lenta, donde un atributo puede ser inicialmente poblado, pero solo se coacciona cuando se accede por primera vez. Esto es importante cuando la coerción es costosa.

En el siguiente ejemplo, que utilizan un tipo unión y método modificadores de hacer esto:

package My::Foo; 
use Moose; 
has x => (
    is => 'rw', 
    isa => 'ArrayRef | Int', 
    required => 1 
); 

around "x" => sub { 
    my $orig = shift; 
    my $self = shift; 
    my $val = $self->$orig(@_); 
    unless(ref($val)) { 
     # Do the cocerion 
     $val = [ map { 1 } 1..$val ]; 
     sleep(1); # in my case this is expensive 
    } 
    return $val; 
}; 
1; 

my $foo = My::Foo->new(x => 4); 
is_deeply $foo->x, [ 1, 1, 1, 1 ], "x converted from int to array at call time"; 

Sin embargo, hay algunos problemas con este:

  1. No me gusta el tipo Unión + modificador de método enfoque. Va en contra de la sugerencia de "Mejores prácticas" al use coercion instead of unions. No es declarativo.

  2. tengo que hacer esto con muchos atributos través muchas clases. Por lo tanto, se necesita alguna forma de DRY. Esto podría ser roles de meta-atributo, coerción de tipo, lo que tienes.

Actualización: Seguí ikegami's sugerencia para encapsular la conversión de tipos caros dentro de un objeto y proporcionar una coacción externa a este objeto:

package My::ArrayFromInt; 
use Moose; 
use Moose::Util::TypeConstraints; 
subtype 'My::ArrayFromInt::Inner', 
    as 'ArrayRef[Int]'; 
coerce 'My::ArrayFromInt::Inner', 
    from 'Int', 
    via { return [ (1) x $_ ] }; 
has uncoerced => (is => 'rw', isa => 'Any', required => 1); 
has value => (
    is  => 'rw', 
    isa  => 'My::ArrayFromInt::Inner', 
    builder => '_buildValue', 
    lazy => 1, 
    coerce => 1 
); 
sub _buildValue { 
    my ($self) = @_; 
    return $self->uncoerced; 
} 
1; 
package My::Foo; 
use Moose; 
use Moose::Util::TypeConstraints; 
subtype 'My::ArrayFromInt::Lazy' => as class_type('My::ArrayFromInt'); 
coerce 'My::ArrayFromInt::Lazy', 
    from 'Int', 
    via { My::ArrayFromInt->new(uncoerced => $_) }; 
has x => (
    is => 'rw', 
    isa => 'My::ArrayFromInt::Lazy', 
    required => 1, 
    coerce => 1 
); 
1; 

Esto funciona si $foo->x->value se llama. Sin embargo, esto no resuelve el punto n. ° 2, ya que necesitaría crear My::ArrayFromInt y el subtipo ::Lazy para cada atributo que me gustaría transformar. Y me gustaría evitar llamar al $foo->x->value si es posible.

+1

Si hay dos formas de representar un dato, uno debería poder obtener cualquiera de las representaciones. Coaccione en un objeto, luego busque los datos del objeto en el formato que desee. [Ejemplo] (http://stackoverflow.com/questions/10506416/can-i-use-an-attribute-modifer-in-moose-in-a-base-class-to-handle-multiple-attri/10508753# 10508753) – ikegami

+0

s/'map {1} 1 .. $ val' /' (1) x $ val'/ – ikegami

+0

@ikegami El problema es que la coacción es costosa; Solo quiero realizarlo si se solicita el atributo. – devoid

Respuesta

0

¿Qué hay de tener el typedef lo largo de las líneas descritas, a continuación, haciendo

has _x => (
    is  => 'ro', 
    isa  => 'Int|MyArrayOfInts', 
    init_arg => 'x', 
    required => 1, 
); 

has x => (
    is => 'ro', 
    lazy => 1, 
    isa => 'MyArrayOfInts', 
    coerce => 1, 
    default => sub { $_[0]->_x }, 
); 

Sería tiene sentido para envolver que hasta en algún tipo de método de ayuda para crear el par de objetos a lo largo de las líneas de

has_lazily_coerced x => (
    is => 'ro', 
    isa => 'TargetType', 
); 

que introspeccionaría en TargetType para obtener una lista de tipos legales para el atributo oculto no coercitivo y generar el par de atributos para usted.