I'd split out type constraint definitions into a separate type library module. Here's a quick example:
use strict;
use warnings;
# thanks.pm is a shim for declaring multiple packages in the same file
+.
# It's on CPAN if you want it. Otherwise, just split them into separat
+e
# files.
no thanks qw(
MyApp::Types
MyApp::Printer
MyApp::Printer::ThreeColour
MyApp::Printer::FourColour
);
BEGIN {
package MyApp::Types;
use Type::Library -base, -declare => qw( ThreeColour FourColour );
use Type::Utils;
use Types::Standard -types;
use Types::ReadOnly -types;
declare ThreeColour, as Locked[
Dict[
cyan => Optional[Num],
magenta => Optional[Num],
yellow => Optional[Num],
]
], coercion => 1;
# black is referred to as "key" in CYMK printing. presumably becau
+se
# using a name that abbreviated to "B" would be confused with "blu
+e".
declare FourColour, as Locked[
Dict[
@{ ThreeColour->parent->wrapped->parameters }, # voodoo?
key => Optional[Num],
]
], coercion => 1;
};
BEGIN {
package MyApp::Printer;
use Moose::Role;
# Let's assume the printer is only capable of printing one pixel.
has pixel => (is => 'ro');
sub dump {
require Data::Dumper;
Data::Dumper::Dumper(@_);
}
};
BEGIN {
package MyApp::Printer::ThreeColour;
use MyApp::Types -types;
use Moose;
with qw(MyApp::Printer);
has '+pixel' => (isa => ThreeColour, coerce => 1);
};
BEGIN {
package MyApp::Printer::FourColour;
use MyApp::Types -types;
use Moose;
with qw(MyApp::Printer);
has '+pixel' => (isa => FourColour, coerce => 1);
};
my $printer = 'MyApp::Printer::FourColour'->new(
pixel => { cyan => 9.4, magenta => 7, yellow => 4 },
);
print($printer->dump);
$printer->pixel->{key} = 9; # ok
print($printer->dump);
$printer->pixel->{black} = 6; # dies
use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name