此答案调查错误发生的原因
首先,我们需要记住 Moose 的类型约束是全局的。 Moose 自己会跟踪它们。
似乎 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 中再次创建
让我们看看当 main.pl 运行时发生了什么。我在下面的分析中忽略了 Moose 本身和其他模块,这是高度简化的。
-
编译时间:main.pl 第 1 行
use Moose::Util::TypeConstraints;
-
编译时间:main.pl 第 6 行
use noUse;
-
编译时间:noUse.pm 第 2 行
use Use1;
-
编译时间:Use1.pm第2行
use Moose;
- Moose 加载完毕,发生了很多事情,我们忽略了这些
-
运行时间:Use1.pm 第 3 行
has 'object1' ... 将属性 object1 放入 Use1 类中。此时 Moose 检查是否存在 mySpecialType 的类型约束,然后使用 isa 检查,因为它假定这是一个包名称。
-
编译时间:noUse.pm 第 3 行
use Use2;
-
编译时间:Use2.pm第2行
use Moose;
- Moose 加载完毕,发生了很多事情,我们忽略了这些
-
运行时间:Use2.pm 第 3 行
has 'object2' ... 将属性 object2 放入 Use2 类中。此时 Moose 会检查 mySpecialType 是否存在类型约束(因为它是在 Use1.pm 中创建的)。
-
运行时间:main.pl line 2ff
subtype 'mySpecialType' ... 尝试创建类型约束 mySpecialType。这失败了,因为已经有一个。它是在Use1.pm 创建的。轰隆隆。
所以问题是 main.pl 中的 used 包中的代码在 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 为该课程的课程提供所需的一切。