5

像 Perl5 中的大多数东西一样,有很多方法可以创建一个支持自定义类型强制的类的属性。这是一个简单的,从数组引用到哈希:

#!/usr/bin/env perl

package Local::Class {
  use Moo;
  use Types::Standard qw( HashRef ArrayRef );

  has set => (
    is => 'ro',
    coerce => 1,
    isa => HashRef->plus_coercions(
      ArrayRef, sub { return { map { $_ => 1} @{$_[0]} } },
    ),
  );
}

my $o = Local::Class->new({ set => [qw( a b b c )] });
# $o->set now holds { a => 1, b => 1, c => 1}

我一直在尝试将这样的东西移植到 Perl6,似乎我需要的是一种将一个强制Array转换为SetHash. 到目前为止,我能够做到这一点的唯一方法是这样的:

#!/usr/bin/env perl6

class Local::Class {
  has %.set;
  ## Wanted to have
  # has %.set is SetHash;
  ## but it dies with "Cannot modify an immutable SetHash"

  submethod TWEAK (:$set) {
    self.set = $set.SetHash;
  }
}

my $o = Local::Class.new( set => [< a b b c >] );
# $o.set now holds {:a, :b, :c}

但在我看来,这似乎不是正确的方法,至少对于将奇数列表传递给构造函数会使脚本死亡的微小细节。

那么,这是如何在 Perl6 中完成的呢?为类属性实现自定义类型强制的推荐方法是什么(因为我确定不止一种)?

4

1 回答 1

5

TWEAK在对象被初始化之后运行BUILD,这就是提供奇数数组的地方。

将强制转换为BUILD时间,事情应该按预期工作:

class Local::Class {
  has %.set;
  submethod BUILD (:$set) {
    %!set := $set.SetHash;
  }
}

如果您可以将强制参数类型与自动属性初始化结合使用会很好,但这会失败,因为 sigil 是属性名称的一部分,并且如果您想接受非关联类型SetHash(),参数不能是-sigilled。%

但是,如果您使用$-sigiled 属性,它可以正常工作:

class Local::Class {
  has $.set;
  submethod BUILD (SetHash() :$!set) {}
}

正如@smls 指出的那样,以下变体

class Local::Class {
  has %.set is SetHash;
  submethod BUILD (:$set) {
    %!set = $set.SetHash;
  }
}

可能应该也可以正常工作,而不是死于无法修改不可变的 SetHash

作为SetHash不可分配的解决方法,您可以使用

class Local::Class {
  has %.set is SetHash;
  submethod BUILD (:$set) {
    %!set{$set.SetHash.keys} = True xx *;
  }
}
于 2016-12-02T02:52:59.253 回答