View file File name : UsingWithMoo3.pod Content :=pod =encoding utf-8 =head1 NAME Type::Tiny::Manual::UsingWithMoo3 - alternative use of Type::Tiny with Moo =head1 MANUAL =head2 Type Registries In all the examples so far, we have imported a collection of type constraints into each class: package Horse { use Moo; use Types::Standard qw( Str ArrayRef HashRef Int Any InstanceOf ); use Types::Common::Numeric qw( PositiveInt ); use Types::Common::String qw( NonEmptyStr ); has name => ( is => 'ro', isa => Str ); has father => ( is => 'ro', isa => InstanceOf["Horse"] ); ...; } This creates a bunch of subs in the Horse namespace, one for each type. We've used L<namespace::autoclean> to clean these up later. But it is also possible to avoid pulling all these into the Horse namespace. Instead we'll use a type registry: package Horse { use Moo; use Type::Registry qw( t ); t->add_types('-Standard'); t->add_types('-Common::String'); t->add_types('-Common::Numeric'); t->alias_type('InstanceOf["Horse"]' => 'Horsey'); has name => ( is => 'ro', isa => t('Str') ); has father => ( is => 'ro', isa => t('Horsey') ); has mother => ( is => 'ro', isa => t('Horsey') ); has children => ( is => 'ro', isa => t('ArrayRef[Horsey]') ); ...; } You don't even need to import the C<< t() >> function. Types::Registry can be used in an entirely object-oriented way. package Horse { use Moo; use Type::Registry; my $reg = Type::Registry->for_me; $reg->add_types('-Standard'); $reg->add_types('-Common::String'); $reg->add_types('-Common::Numeric'); $reg->alias_type('InstanceOf["Horse"]' => 'Horsey'); has name => ( is => 'ro', isa => $reg->lookup('Str') ); ...; } You could create two registries with entirely different definitions for the same named type. my $dracula = Aristocrat->new(name => 'Dracula'); package AristocracyTracker { use Type::Registry; my $reg1 = Type::Registry->new; $reg1->add_types('-Common::Numeric'); $reg1->alias_type('PositiveInt' => 'Count'); my $reg2 = Type::Registry->new; $reg2->add_types('-Standard'); $reg2->alias_type('InstanceOf["Aristocrat"]' => 'Count'); $reg1->lookup("Count")->assert_valid("1"); $reg2->lookup("Count")->assert_valid($dracula); } Type::Registry uses C<AUTOLOAD>, so things like this work: $reg->ArrayRef->of( $reg->Int ); Although you can create as many registries as you like, Type::Registry will create a default registry for each package. # Create a new empty registry. # my $reg = Type::Registry->new; # Get the default registry for my package. # It will be pre-populated with any types we imported using `use`. # my $reg = Type::Registry->for_me; # Get the default registry for some other package. # my $reg = Type::Registry->for_class("Horse"); Type registries are a convenient place to store a bunch of types without polluting your namespace. They are not the same as type libraries though. L<Types::Standard>, L<Types::Common::String>, and L<Types::Common::Numeric> are type libraries; packages that export types for others to use. We will look at how to make one of those later. For now, here's the best way to think of the difference: =over =item * Type registry Curate a collection of types for me to use here in this class. This collection is an implementaion detail. =item * Type library Export a collection of types to be used across multiple classes. This collection is part of your API. =back =head2 Importing Functions We've seen how, for instance, Types::Standard exports a sub called C<Int> that returns the B<Int> type object. use Types::Standard qw( Int ); my $type = Int; $type->check($value) or die $type->get_message($value); Type libraries are also capable of exporting other convenience functions. =head3 C<< is_* >> This is a shortcut for checking a value meets a type constraint: use Types::Standard qw( is_Int ); if ( is_Int($value) ) { ...; } Calling C<< is_Int($value) >> will often be marginally faster than calling C<< Int->check($value) >> because it avoids a method call. (Method calls in Perl end up slower than normal function calls.) Using things like C<is_ArrayRef> in your code might be preferable to C<< ref($value) eq "ARRAY" >> because it's neater, leads to more consistent type checking, and might even be faster. (Type::Tiny can be pretty fast; it is sometimes able to export these functions as XS subs.) If checking type constraints like C<is_ArrayRef> or C<is_InstanceOf>, there's no way to give a parameter. C<< is_ArrayRef[Int]($value) >> doesn't work, and neither does C<< is_ArrayRef(Int, $value) >> nor C<< is_ArrayRef($value, Int) >>. For some types like C<is_InstanceOf>, this makes them fairly useless; without being able to give a class name, it just acts the same as C<< is_Object >>. See L</Exporting Parameterized Types> for a solution. Also, check out L<isa>. There also exists a generic C<is> function. use Types::Standard qw( ArrayRef Int ); use Type::Utils qw( is ); if ( is ArrayRef[Int], \@numbers ) { ...; } =head3 C<< assert_* >> While C<< is_Int($value) >> returns a boolean, C<< assert_Int($value) >> will throw an error if the value does not meet the constraint, and return the value otherwise. So you can do: my $sum = assert_Int($x) + assert_Int($y); And you will get the sum of integers C<< $x >> and C<< $y >>, and an explosion if either of them is not an integer! Assert is useful for quick parameter checks if you are avoiding L<Type::Params> for some strange reason: sub add_numbers { my $x = assert_Num(shift); my $y = assert_Num(shift); return $x + $y; } You can also use a generic C<assert> function. use Type::Utils qw( assert ); sub add_numbers { my $x = assert Num, shift; my $y = assert Num, shift; return $x + $y; } =head3 C<< to_* >> This is a shortcut for coercion: my $truthy = to_Bool($value); It trusts that the coercion has worked okay. You can combine it with an assertion if you want to make sure. my $truthy = assert_Bool(to_Bool($value)); =head3 Shortcuts for exporting functions This is a little verbose: use Types::Standard qw( Bool is_Bool assert_Bool to_Bool ); Isn't this a little bit nicer? use Types::Standard qw( +Bool ); The plus sign tells a type library to export not only the type itself, but all of the convenience functions too. You can also use: use Types::Standard -types; # export Int, Bool, etc use Types::Standard -is; # export is_Int, is_Bool, etc use Types::Standard -assert; # export assert_Int, assert_Bool, etc use Types::Standard -to; # export to_Bool, etc use Types::Standard -all; # just export everything!!! So if you imagine the functions exported by Types::Standard are like this: qw( Str is_Str assert_Str Num is_Num assert_Num Int is_Int assert_Int Bool is_Bool assert_Bool to_Bool ArrayRef is_ArrayRef assert_ArrayRef ); # ... and more Then "+" exports a horizonal group of those, and "-" exports a vertical group. =head2 Exporting Parameterized Types It's possible to export parameterizable types like B<ArrayRef>, but it is also possible to export I<parameterized> types. use Types::Standard qw( ArrayRef Int ); use Types::Standard ( '+ArrayRef' => { of => Int, -as => 'IntList' }, ); has numbers => (is => 'ro', isa => IntList); Using C<< is_IntList($value) >> should be significantly faster than C<< ArrayRef->of(Int)->check($value) >>. This trick only works for parameterized types that have a single parameter, like B<ArrayRef>, B<HashRef>, B<InstanceOf>, etc. (Sorry, C<Dict> and C<Tuple>!) =head2 Do What I Mean! use Type::Utils qw( dwim_type ); dwim_type("ArrayRef[Int]") C<dwim_type> will look up a type constraint from a string and attempt to guess what you meant. If it's a type constraint that you seem to have imported with C<use>, then it should find it. Otherwise, if you're using Moose or Mouse, it'll try asking those. Or if it's in Types::Standard, it'll look there. And if it still has no idea, then it will assume dwim_type("Foo") means dwim_type("InstanceOf['Foo']"). It just does a big old bunch of guessing. The C<is> function will use C<dwim_type> if you pass it a string as a type. use Type::Utils qw( is ); if ( is "ArrayRef[Int]", \@numbers ) { ...; } =head1 NEXT STEPS You now know pretty much everything there is to know about how to use type libraries. Here's your next step: =over =item * L<Type::Tiny::Manual::Libraries> Defining your own type libraries, including extending existing libraries, defining new types, adding coercions, defining parameterizable types, and the declarative style. =back =head1 AUTHOR Toby Inkster E<lt>tobyink@cpan.orgE<gt>. =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2013-2014, 2017-2021 by Toby Inkster. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 DISCLAIMER OF WARRANTIES THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. =cut