【问题标题】:Lazy Attribute Coercion惰性属性强制
【发布时间】:2012-05-19 08:46:49
【问题描述】:

使用Moose,您可以在属性上使用lazy builders,在第一次访问该属性时调用构建器如果该属性尚未填充。您可以使用coerce 对属性进行类型强制,但是只要设置了属性就会应用此功能,即使在对象初始化时也是如此。

我正在寻找一种实现 惰性强制 的方法,其中最初可能会填充属性,但仅在首次访问时才强制执行。当强制执行代价高昂时,这一点很重要。

在以下示例中,我使用联合类型和方法修饰符来执行此操作:

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";

但是这样做有一些问题:

  1. 我不喜欢 联合类型 + 方法修饰符 的方法。它违背了use coercion instead of unions 的“最佳实践”建议。它不是声明性的。

  2. 我需要使用 many 类中的 many 属性来执行此操作。因此需要某种形式的 DRY。这可能是元属性角色,类型强制,你有什么。

更新: 我按照ikegami's 的建议将昂贵的类型强制封装在一个对象内部,并为这个对象提供一个外部强制:

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;

如果调用了$foo->x->value,这将起作用。但是,这并不能解决第 2 点,因为我需要为要转换的每个属性创建 My::ArrayFromInt::Lazy 子类型。如果可能的话,我想避免打电话给$foo->x->value

【问题讨论】:

  • 如果有两种表示基准的方式,应该能够得到任何一种表示。强制转换为对象,然后以您想要的格式从对象中获取数据。 Example
  • s/ map { 1 } 1..$val / (1) x $val /
  • @ikegami 问题是强制很昂贵;我只想在请求属性时执行它。
  • 这就是为什么我说要强制一个对象(有一个执行强制的 getter)而不是进行昂贵的强制。

标签: perl moose lazy-evaluation


【解决方案1】:

如何按照描述的方式使用 typedef,然后执行

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 },
);

将它包装成某种辅助方法来创建对象对是有意义的

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

它将自省 TargetType 以获取未强制影子属性的合法类型列表并为您生成一对属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-02
    • 2021-02-08
    • 2017-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多