我想在我的应用程序中使用Moose::Util::TypeConstraints
。
所以我在main.pl
use Moose::Util::TypeConstraints;
subtype 'mySpecialType'
=> as 'Object'
=> where sub { $_->does('something') };
use noUse;
在包noUse.pm
中使用的包是使用类型约束的
package noUse;
use Use1;
use Use2;
1;
我的包Use1
和Use2
正在使用类型约束
package Use1;
use Moose;
has 'object1' => ( is => 'ro', isa => 'mySpecialType' );
1;
package Use2;
use Moose;
has 'object2' => ( is => 'ro', isa => 'mySpecialType' );
1;
如果我运行main.pl
,我会收到此错误:
类型约束' mySpecialType'已在Use1中创建,无法再在main中再次创建/usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Util/TypeConstraints.pm第348行 Moose :: Util :: TypeConstraints :: subtype(' mySpecialType',' HASH(0x227f398)',' HASH(0x2261140)')在main调用。第10行
导致此错误的原因是什么?如何解决?
答案 0 :(得分:3)
标准方法是use
来自图书馆所需的公共代码,而不是让它从主程序中散发出来。
package MyTypes;
use Moose::Util::TypeConstraints;
subtype 'mySpecialType'
=> as 'Object'
=> where sub { $_->does('something') };
package Use1;
use Moose;
use MyTypes;
has 'object1' => ( is => 'ro', isa => 'mySpecialType' );
1;
package Use2;
use Moose;
use MyTypes;
has 'object2' => ( is => 'ro', isa => 'mySpecialType' );
1;
main.pl
变为
use noUse;
noUse.pm
保持不变。
答案 1 :(得分:2)
首先,我们需要记住,穆斯的类型约束是全局的。穆斯自己跟踪它们。
似乎Moose自动从isa => 'Foo'
制作Moose :: Util :: TypeConstraints。
我将 main.pl 中的代码更改为以下to check if this constraint already exists。
use noUse;
use Data::Printer { deparse => 1 };
my $type = Moose::Util::TypeConstraints::find_type_constraint('mySpecialType');
p $type;
原来确实如此。
Moose::Meta::TypeConstraint::Class {
Parents Moose::Meta::TypeConstraint
public methods (9) : class, create_child_type, equals, get_message, is_a_type_of, is_subtype_of, meta, new, parents
private methods (1) : _new
internals: {
class "mySpecialType",
compiled_type_constraint sub {
package Eval::Closure::Sandbox_150;
use warnings;
use strict;
do {
$_[0]->isa('mySpecialType') if &Scalar::Util::blessed($_[0])
};
},
constraint sub {
package Moose::Meta::TypeConstraint::Class;
use warnings;
use strict;
$_[0]->isa($class_name);
},
_default_message sub {
package Moose::Meta::TypeConstraint;
use warnings;
use strict;
my $value = shift();
my $can_partialdump = try(sub {
require Devel::PartialDump;
'Devel::PartialDump'->VERSION(0.14);
1;
}
);
if ($can_partialdump) {
$value = 'Devel::PartialDump'->new->dump($value);
}
else {
$value = defined $value ? overload::StrVal($value) : 'undef';
}
return q[Validation failed for '] . $name . "' with value $value";
},
inline_environment {},
inlined sub {
package Moose::Meta::TypeConstraint::Class;
use warnings;
use strict;
my $self = shift();
my $val = shift();
return 'Scalar::Util::blessed(' . $val . ')' . ' && ' . $val . '->isa(' . B::perlstring($self->class) . ')';
},
name "mySpecialType",
package_defined_in "Use1",
parent Moose::Meta::TypeConstraint
}
}
这是一个简单的isa
检查,如果该名称上不存在其他约束,Moose似乎会放入该检查。这样,你可以做这样的事情。
use Moose;
has date => ( is => 'ro', isa => 'DateTime' );
原始 main.pl 中的执行顺序是问题,因为use
在编译时完成。
这是你的错误,重点是。
类型约束' mySpecialType' 已在Use1 中创建,无法在主
中再次创建
让我们来了解 main.pl 运行时的情况。我在下面的分析中忽略了Moose本身和其他模块,这是高度简化的。
use Moose::Util::TypeConstraints;
use noUse;
use Use1;
use Moose;
has 'object1' ...
将属性object1
放入Use1
类。此时,Moose检查mySpecialType
是否存在类型约束,然后使用isa
检查,因为它确认这是包名称。use Use2;
use Moose;
has 'object2' ...
将属性object2
放入Use2
类。此时,Moose检查mySpecialType
是否存在类型约束,并且(因为它是在Use1.pm
中创建的)。subtype 'mySpecialType' ...
尝试创建类型约束mySpecialType
。这失败了,因为已经存在一个。它是在Use1.pm
中创建的。 BOOM。所以问题是 main.pl 中的use
d包中的代码在 main.pl 中的普通代码之前运行。您需要在第一次运行时遇到类型之前声明它们。
让我们做一个肮脏的黑客来证明这一点。
use Moose::Util::TypeConstraints;
BEGIN {
subtype 'mySpecialType' => as 'Object' => where sub { $_->does('something') };
}
use noUse;
这不会爆炸,因为BEGIN
块在编译时运行,这在use noUse
语句之前。因此,当Moose第一次在has => ( ... isa => 'mySpecialType' )
构造中遇到它时,类型约束已经存在。
但那并不漂亮。让我们尝试另一种解决方案。 使用Moose :: Util :: TypeConstraints;
subtype 'mySpecialType' => as 'Object' => where sub { $_->does('something') };
require noUse;
这也可以,因为require
不是在编译时调用,而是在运行时。因此首先安装subtype
,然后加载noUse
。但这会延迟加载你的所有课程,直到我们已经在你的主程序中运行时,这是不好的形式。
他们正确的方法是将所有类型放入类型库并首先加载它。有关如何执行此操作,请参阅choroba's answer。
为了更好地衡量,您还应该use
在使用任何类型的所有模块中键入库模块。这样,您可以确保将来在编写具有相同类的其他应用程序时,您不需要记住加载该类型库。 始终use
该课程中课程所需的一切。