类型::微小和深刻的强制

Per*_*erC 9 perl moo

我试图让深刻的强制工作Type::Tiny没有任何成功.从手册中可以说:

"某些参数化类型约束可以自动获取强制,如果它们的参数有强制.例如: ArrayRef[Int->plus_coercions(Num, q{int($_)}) ] ......做你的意思!"

我试图完成的是得到这样的"做我的意思":

package Person;
use Types::Standard -types;
use Moo;

has name => (is => 'ro', isa => Str);

package Family;
use Types::Standard -types;
use Moo;

has members => (is => 'ro', isa => ArrayRef[InstanceOf['Person']]);

package main;

my $family = Family->new(members => [
  'mom',
  Person->new(name => 'dad'),
  Person->new(name => 'girl'),
  'dog'
]);
Run Code Online (Sandbox Code Playgroud)

Family使用元素进行实例化时,Str它们应该被自动强制转换为Person对象.我尝试了一系列不同的想法(plus_coercions,类型库等),没有任何运气.他们都以同样的方式失败.

使用plus_coercions时(从StrObject)

package Family;

has members => (
  is => 'ro',
  isa => ArrayRef[ Object->plus_coercions(Str, q{ Person->new(name => $_) }) ],
);
Run Code Online (Sandbox Code Playgroud)

Type :: Tiny引发异常:

Reference ["mom",bless( {"name" => "dad"}, 'Person' ),bless( {"name" =...] did not pass type constraint "ArrayRef[Object]" (in $args->{"members"})
"ArrayRef[Object]" constrains each value in the array with "Object"
"Object" is a subtype of "Object"
"Object" is a subtype of "Ref"
Value "mom" did not pass type constraint "Ref" (in $args->{"members"}->[0])
"Ref" is defined as: (!!ref($_))
Run Code Online (Sandbox Code Playgroud)

我知道我可以通过修改Family->new使用BUILDARGSsub in 的参数来解决这个问题Family,但如果Type :: Tiny可以自动执行此操作,那么它会很简洁.

更新

感谢Tobys友好的帮助,我得到了这个工作.困扰我的唯一部分是使用ArrayRef[Object]而不是正确ArrayRef[InstanceOf['Person']](InstanceOf没有任何plus_coercions).随着Object实例任何类可以被插入members,这肯定不是你想要的.

通过制作一个来解决这个问题class_type.这是完整的工作代码:

package Person;
use Types::Standard -types;
use Moo;

has name => (is => 'ro', isa => Str);

package Family;
use Types::Standard -types;
use Type::Utils -all;
use Moo;

my $Person = class_type { class => 'Person' };

my $Members = ArrayRef[
  $Person->plus_coercions(Str, q{ Person->new(name => $_) })
];

has members => (
  is     => 'ro',
  isa    => $Members,
  coerce => $Members->coercion,
);

sub list { join(', ', map { $_->name } @{ $_[0]->members }) }

package main;

my $family = Family->new(members => [
  'mom',
  Person->new(name => 'dad'),
  Person->new(name => 'girl'),
  'dog'
]);

print $family->list, "\n";
Run Code Online (Sandbox Code Playgroud)

mom, dad, girl, dog运行时打印得很好.

tob*_*ink 5

Moose/Moo/Mouse属性默认情况下不强制,因此即使类型约束具有强制,您也需要告诉属性使用该强制!

如果你使用的是Moose或Mouse,你可以这样做:

has members => (
  is     => 'ro',
  isa    => ArrayRef[ Object->plus_coercions(Str, q{ Person->new(name => $_) }) ],
  coerce => 1,
);
Run Code Online (Sandbox Code Playgroud)

但是Moo不支持coerce=>1; 相反,它期望coderef或重载对象充当强制.Type :: Tiny可以通过调用为您提供合适的重载对象$type->coercion.

# Let's store the type constraint in a variable to avoid repetition...
my $type = ArrayRef[
  Object->plus_coercions(  Str, q{Person->new(name => $_)}  )
];

has members => (
  is     => 'ro',
  isa    => $type,
  coerce => $type->coercion,
);
Run Code Online (Sandbox Code Playgroud)