Constraint of type "XYZ" has already been created

I want to use Moose::Util::TypeConstraints

in my application.

So I define one in my main.pl

main.pl

use Moose::Util::TypeConstraints;

subtype 'mySpecialType'
    => as 'Object'
    => where sub { $_->does('something') };

use noUse;

      

The package noUse.pm

uses packages that use a constraint like

noUse.pm

package noUse;

use Use1;

use Use2;

1;

      

and my package Use1

and Use2

work with a constraint like

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;

      

If I run main.pl

I get this error:

A constraint of type 'mySpecialType' has already been created in Use1 and cannot be re-created in main at / usr / lib / x 86_64-linux-gnu / perl5 / 5.22 / Moose / Util / TypeConstraints.pm line 348 Moose :: Util :: TypeConstraints :: subtype ('mySpecialType', 'HASH (0x227f398)', 'HASH (0x2261140)') called on line main.pl 10

What is causing this error and how can I fix it?

+1


source to share


2 answers


The standard way is to use

share code from libraries where you need it, rather than letting it emit from the main program.

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;

      

And main.pl

it becomes simple

use noUse;

      

noUse.pm

remains unchanged.

See simbabque's answer for what.

+3


source


This answer investigates why the error occurs

First of all, we must remember that constraints like Moose are global. Moose himself tracks them.

Moose seems to automatically make Moose :: Util :: TypeConstraints from isa => 'Foo'

.

I changed my code in main.pl to the following to check if this limitation exists .

use noUse;
use Data::Printer { deparse => 1 };
my $type = Moose::Util::TypeConstraints::find_type_constraint('mySpecialType');
p $type;

      

It turns out it does it.

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
    }
}

      

This is a simple check isa

that seems to show up in Moose if no other restriction already exists with that name. So you can do things like this.

use Moose;
has date => ( is => 'ro', isa => 'DateTime' );

      

The order of execution in our original main.pl is a problem because it use

is executed at compile time.

Here's your mistake again, with emphasis.

constraint of type 'mySpecialType' has already been created in Use1 and cannot be created again in main



Let's see what happens when main.pl starts up. I am ignoring Moose itself and other modules in the analysis below, which is very simplistic.

  • compile time: main.pl line 1 use Moose::Util::TypeConstraints;

    • this module is loaded, a lot of things happen, we ignore those
  • compile time: main.pl line 6 use noUse;

    • compile time: noUse.pm line 2 use Use1;

      • compile time: Use1.pm line 2 use Moose;

        • Moose is loading, a lot of things are happening, we ignore these
      • runtime: Use1.pm line 3 has 'object1' ...

        puts an attribute object1

        on a class Use1

        . At this point, Moose checks to see if a type constraint exists for mySpecialType

        , and then does it with a check isa

        , because that means it's a package name.
    • compile time: noUse.pm line 3 use Use2;

      • compile time: Use2.pm line 2 use Moose;

        • Moose is loading, a lot of things are happening, we ignore these
      • runtime: Use2.pm line 3 has 'object2' ...

        puts an attribute object2

        on a class Use2

        . At this point, Moose checks to see if the type constraint for mySpecialType

        and is (because it was created in Use1.pm

        ).
  • runtime: main.pl line 2ff subtype 'mySpecialType' ...

    tries to create a type constraint mySpecialType

    . This fails because he is no longer there. It was created in Use1.pm

    . BOOM.

So the problem is that the code in the use

d packages in main.pl runs before the normal code in main.pl. You need to declare your types before they are encountered at runtime the first time.

Prove it with a dirty hack.

use Moose::Util::TypeConstraints;

BEGIN {
    subtype 'mySpecialType' => as 'Object' => where sub { $_->does('something') };
}

use noUse;

      

It won't blow up because the block is BEGIN

run at compile time, which is before the statement use noUse

. Therefore, the type constraint will already be there when Moose first encounters it in a design has => ( ... isa => 'mySpecialType' )

.

But this is not very good. try another solution. use Moose :: Util :: TypeConstraints;

subtype 'mySpecialType' => as 'Object' => where sub { $_->does('something') };

require noUse;

      

This will also work because it is require

not called at compile time, but at run time. Therefore, it is first installed subtype

and then loaded noUse

. But this delays loading all your classes until we have finished your main program, which is bad form.

The correct way to do this is to put all your types in a type library and load them first. See choroba's answer for how to do this.

For good measure, you should also use

create a library module in all modules that use any of the types. This way, you ensure that in the future, when you write other applications with the same classes, you don't need to forget to load a library of that type. Always use

everything you need for a class in that class.

+2


source







All Articles