类型约束' XYZ'已经创建了

时间:2017-01-09 14:54:15

标签: perl types moose

我想在我的应用程序中使用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;

我的包Use1Use2正在使用类型约束

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,我会收到此错误:

  

类型约束' 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行

导致此错误的原因是什么?如何解决?

2 个答案:

答案 0 :(得分:3)

标准方法是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' answer了解原因。

答案 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本身和其他模块,这是高度简化的。

  • 编译时间 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 第2ff行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该课程中课程所需的一切。