【问题标题】:The type constraint 'XYZ' has already been created类型约束“XYZ”已创建
【发布时间】:2017-01-10 17:13:53
【问题描述】:

我想在我的应用程序中使用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 正在使用类型约束

使用1.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.pl 第 10 行调用

是什么导致了这个错误,我该如何解决?

【问题讨论】:

    标签: perl types moose


    【解决方案1】:

    标准的方法是use需要它的库中的公共代码,而不是让它从主程序中散发出来。

    MyTypes.pm

    package MyTypes;
    use Moose::Util::TypeConstraints;
    subtype 'mySpecialType'
        => as 'Object'
        => where sub { $_->does('something') };
    

    使用1.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 了解原因。

    【讨论】:

    • 让我们合并。我现在几乎要把它放在我的解释的底部。 :D
    • @simbabque:是的,我想添加一段关于编译时发生的事情以及 Moose 如何解决未知类型约束 :)
    • 我将链接到您的答案。
    • 现在我们有了一个循环引用:D
    【解决方案2】:

    此答案调查错误发生的原因

    首先,我们需要记住 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 为该课程的课程提供所需的一切。

    【讨论】:

    • @Borodin 您在编辑时不小心删除了我的一半答案;)
    • 糟糕 :( 交给你了!
    • @Borodin 不用担心。感谢您尝试提供帮助。 :)
    • 所以要清楚,您是说名为mySpecialType 的约束已经硬编码到Moose 中?太可怕了。
    • 没有@borodin。我是说,当基于 Moose 的类中的属性具有 isa 值而不是类型约束时,Moohse Moose 会创建一个具有该名称的属性来执行 isa 检查。我的调试输出中的 deparse 表明了这一点。如果在您自己安装之前发生这种情况,您将无法再创建它,因为您似乎无法覆盖它们。
    猜你喜欢
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 2012-09-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-21
    相关资源
    最近更新 更多