—=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 implementation 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 horizontal 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>!)
Since Type::Tiny 2.8.0 there has been experimental support for a
different way of exporting parameterized versions of certain parameterizable
type constraints.
use Types::Standard -types;
use Types::Standard::ArrayRef (
IntList => { of => Int },
StrList => { of => Str },
NonEmptyStrList => { of => Str->where( sub { length($_) > 0 } ) },
StrNonEmptyList => { of => Str, where => sub { @$_ > 0 } },
NEStrNEList => {
of => Str->where( sub { length($_) > 0 } ),
where => sub { @$_ > 0 },
},
);
assert_IntList [ 1.. 5 ]; # doesn't die
is_NonEmptyStrList [ "foo", "bar" ]; # ==> true
is_NonEmptyStrList [ "foo", "" ]; # ==> false
is_NonEmptyStrList []; # ==> true
is_StrNonEmptyList [ "foo", "bar" ]; # ==> true
is_StrNonEmptyList [ "foo", "" ]; # ==> true
is_StrNonEmptyList []; # ==> false
is_NEStrNEList [ "foo", "bar" ]; # ==> true
is_NEStrNEList [ "foo", "" ]; # ==> false
is_NEStrNEList []; # ==> false
And of course C<where> also accepts a string of Perl code.
=head2 Lexical imports
Type::Tiny 2.0 combined with Perl 5.37.2+ allows lexically scoped imports.
So:
my $is_ok = do {
use Types::Standard -lexical, qw( Str ArrayRef );
ArrayRef->of( Str )->check( \@things );
};
# The Str and ArrayRef types aren't defined here.
=head2 Do What I Mean!
use Type::Utils qw( dwim_type );
my $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 ) {
...;
}
=head2 Types::Common
Notice that in a lot of examples we're importing one or two functions
each from a few different modules:
use Types::Common::Numeric qw( PositiveInt );
use Types::Common::String qw( NonEmptyStr );
use Types::Standard qw( ArrayRef Slurpy );
use Type::Params qw( signature_for );
A module called L<Types::Common> exists which acts as a single place you
can use for importing most of Type::Tiny's commonly used types and functions.
use Types::Common qw(
PositiveInt NonEmptyStr ArrayRef Slurpy
signature_for
);
Types::Common provides:
=over
=item *
All the types from L<Types::Standard>.
=item *
All the types from L<Types::Common::Numeric> and L<Types::Common::String>.
=item *
All the types from L<Types::TypeTiny>.
=item *
The C<< -sigs >> tag from L<Type::Params>.
=item *
The C<< t() >> function from L<Type::Registry>.
=back
=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-2025 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