类型约束 'XYZ' 已创建
The type constraint 'XYZ' has already been created
我想在我的应用程序中使用 Moose::Util::TypeConstraints
。
所以我在 main.pl
中定义了一个
main.pl
use Moose::Util::TypeConstraints;
subtype 'mySpecialType'
=> as 'Object'
=> where sub { $_->does('something') };
use noUse;
包noUse.pm
中使用的包,使用类型约束
noUse.pm
package noUse;
use Use1;
use Use2;
1;
和我的包 Use1
和 Use2
正在使用类型约束
Use1.pm
package Use1;
use Moose;
has 'object1' => ( is => 'ro', isa => 'mySpecialType' );
1;
Use2.pm
package Use2;
use Moose;
has 'object2' => ( is => 'ro', isa => 'mySpecialType' );
1;
如果我 运行 main.pl
我得到这个错误:
The type constraint 'mySpecialType' has already been created in Use1 and cannot be created again in main at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Util/TypeConstraints.pm line 348
Moose::Util::TypeConstraints::subtype('mySpecialType', 'HASH(0x227f398)', 'HASH(0x2261140)') called at main.pl line 10
是什么原因导致此错误,我该如何解决?
此答案调查错误发生的原因
首先,我们需要记住 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
在 编译时完成 [=131] =].
这里又是你的错误,重点是。
type constraint 'mySpecialType' has already been created in Use1 and cannot be created again in 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 加载,发生了很多事情,我们忽略那些
- 运行 time: Use1.pm line 3
has 'object1' ...
puts attribute object1
变成 Use1
class。此时 Moose 检查是否存在 mySpecialType
的类型约束,然后使用 isa
检查,因为它假设这是一个包名称。
- 编译时:noUse.pm第3行
use Use2;
- 编译时:Use2.pm第2行
use Moose;
- Moose 加载,发生了很多事情,我们忽略那些
- 运行 time: Use2.pm line 3
has 'object2' ...
puts attribute object2
变成 Use2
class。此时 Moose 检查是否存在 mySpecialType
的类型约束并且存在(因为它是在 Use1.pm
中创建的)。
- 运行 time: main.pl line 2ff
subtype 'mySpecialType' ...
尝试创建类型约束mySpecialType
。这失败了,因为已经有一个。它创建于 Use1.pm
。砰。
所以问题是 main.pl 中 use
d 的包中的代码在 运行 中的正常代码之前得到 运行 =81=]main.pl。您需要在 运行 时间 第一次遇到它们之前声明您的类型。
让我们通过一个肮脏的 hack 来证明这一点。
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
不是在 编译时 调用,而是在 运行 时 [=131] =].所以先安装 subtype
,然后加载 noUse
。但这会延迟加载所有 classes,直到我们已经在您的主程序中进入 运行 时间,这是错误的形式。
他们这样做的正确方法 是将所有类型放入类型库,然后先加载那个。请参阅 了解如何操作。
为了更好地衡量,您还应该 use
在使用任何类型的所有模块中使用该类型库模块。这样您就可以确保将来,当您使用相同的 classes 编写其他应用程序时,您不需要记住加载该类型库。 始终 use
class 所需的一切 class。
标准的做法是use
需要的库中的公共代码,而不是让它从主程序中辐射出来。
MyTypes.pm
package MyTypes;
use Moose::Util::TypeConstraints;
subtype 'mySpecialType'
=> as 'Object'
=> where sub { $_->does('something') };
Use1.pm
package Use1;
use Moose;
use MyTypes;
has 'object1' => ( is => 'ro', isa => 'mySpecialType' );
1;
Use2.pm
package Use2;
use Moose;
use MyTypes;
has 'object2' => ( is => 'ro', isa => 'mySpecialType' );
1;
而 main.pl
变成
use noUse;
noUse.pm
保持不变。
请参阅 simbabque's 了解原因。
我想在我的应用程序中使用 Moose::Util::TypeConstraints
。
所以我在 main.pl
main.pl
use Moose::Util::TypeConstraints;
subtype 'mySpecialType'
=> as 'Object'
=> where sub { $_->does('something') };
use noUse;
包noUse.pm
中使用的包,使用类型约束
noUse.pm
package noUse;
use Use1;
use Use2;
1;
和我的包 Use1
和 Use2
正在使用类型约束
Use1.pm
package Use1;
use Moose;
has 'object1' => ( is => 'ro', isa => 'mySpecialType' );
1;
Use2.pm
package Use2;
use Moose;
has 'object2' => ( is => 'ro', isa => 'mySpecialType' );
1;
如果我 运行 main.pl
我得到这个错误:
The type constraint 'mySpecialType' has already been created in Use1 and cannot be created again in main at /usr/lib/x86_64-linux-gnu/perl5/5.22/Moose/Util/TypeConstraints.pm line 348 Moose::Util::TypeConstraints::subtype('mySpecialType', 'HASH(0x227f398)', 'HASH(0x2261140)') called at main.pl line 10
是什么原因导致此错误,我该如何解决?
此答案调查错误发生的原因
首先,我们需要记住 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
在 编译时完成 [=131] =].
这里又是你的错误,重点是。
type constraint 'mySpecialType' has already been created in Use1 and cannot be created again in 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 加载,发生了很多事情,我们忽略那些
- 运行 time: Use1.pm line 3
has 'object1' ...
puts attributeobject1
变成Use1
class。此时 Moose 检查是否存在mySpecialType
的类型约束,然后使用isa
检查,因为它假设这是一个包名称。
- 编译时:Use1.pm第2行
- 编译时:noUse.pm第3行
use Use2;
- 编译时:Use2.pm第2行
use Moose;
- Moose 加载,发生了很多事情,我们忽略那些
- 运行 time: Use2.pm line 3
has 'object2' ...
puts attributeobject2
变成Use2
class。此时 Moose 检查是否存在mySpecialType
的类型约束并且存在(因为它是在Use1.pm
中创建的)。
- 编译时:Use2.pm第2行
- 编译时:noUse.pm第2行
- 运行 time: main.pl line 2ff
subtype 'mySpecialType' ...
尝试创建类型约束mySpecialType
。这失败了,因为已经有一个。它创建于Use1.pm
。砰。
所以问题是 main.pl 中 use
d 的包中的代码在 运行 中的正常代码之前得到 运行 =81=]main.pl。您需要在 运行 时间 第一次遇到它们之前声明您的类型。
让我们通过一个肮脏的 hack 来证明这一点。
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
不是在 编译时 调用,而是在 运行 时 [=131] =].所以先安装 subtype
,然后加载 noUse
。但这会延迟加载所有 classes,直到我们已经在您的主程序中进入 运行 时间,这是错误的形式。
他们这样做的正确方法 是将所有类型放入类型库,然后先加载那个。请参阅
为了更好地衡量,您还应该 use
在使用任何类型的所有模块中使用该类型库模块。这样您就可以确保将来,当您使用相同的 classes 编写其他应用程序时,您不需要记住加载该类型库。 始终 use
class 所需的一切 class。
标准的做法是use
需要的库中的公共代码,而不是让它从主程序中辐射出来。
MyTypes.pm
package MyTypes;
use Moose::Util::TypeConstraints;
subtype 'mySpecialType'
=> as 'Object'
=> where sub { $_->does('something') };
Use1.pm
package Use1;
use Moose;
use MyTypes;
has 'object1' => ( is => 'ro', isa => 'mySpecialType' );
1;
Use2.pm
package Use2;
use Moose;
use MyTypes;
has 'object2' => ( is => 'ro', isa => 'mySpecialType' );
1;
而 main.pl
变成
use noUse;
noUse.pm
保持不变。
请参阅 simbabque's