Edit file File name : Libraries.pod Content :=pod =encoding utf-8 =head1 NAME Type::Tiny::Manual::Libraries - defining your own type libraries =head1 MANUAL =head2 Defining a Type A type is an object and you can create a new one using the constructor: use Type::Tiny; my $type = Type::Tiny->new(%args); A full list of the available arguments can be found in the L<Type::Tiny> documentation, but the most important ones to begin with are: =over =item C<name> The name of your new type. Type::Tiny uses a convention of UpperCamelCase names for type constraints. The type name may also begin with one or two leading underscores to indicate a type intended for internal use only. Types using non-ASCII characters may cause problems on older versions of Perl (pre-5.8). Although this is optional and types may be anonymous, a name is required for a type constraint to added to a type library. =item C<constraint> A code reference checking C<< $_ >> and returning a boolean. Alternatively, a string of Perl code may be provided. If you've been paying attention, you can probably guess that the string of Perl code may result in more efficient type checks. =item C<parent> An existing type constraint to inherit from. A value will need to pass the parent constraint before its own constraint would be called. my $Even = Type::Tiny->new( name => 'EvenNumber', parent => Types::Standard::Int, constraint => sub { # in this sub we don't need to check that $_ is an Int # because the parent will take care of that! $_ % 2 == 0 }, ); Although the C<parent> is optional, it makes sense whenever possible to inherit from an existing type constraint to benefit from any optimizations or XS implementations they may provide. =back =head2 Defining a Library A library is a Perl module that exports type constraints as subs. L<Types::Standard>, L<Types::Common::Numeric>, and L<Types::Common::String> are type libraries that are bundled with Type::Tiny. To create a type library, create a package that inherits from L<Type::Library>. package MyTypes { use Type::Library -base; ...; # your type definitions go here } The C<< -base >> flag is just a shortcut for: package MyTypes { use Type::Library; our @ISA = 'Type::Library'; } You can add types like this: package MyTypes { use Type::Library -base; my $Even = Type::Tiny->new( name => 'EvenNumber', parent => Types::Standard::Int, constraint => sub { # in this sub we don't need to check that $_ is an Int # because the parent will take care of that! $_ % 2 == 0 }, ); __PACKAGE__->add_type($Even); } There is a shortcut for adding types if they're going to be blessed L<Type::Tiny> objects and not, for example, a subclass of Type::Tiny. You can just pass C<< %args >> directly to C<add_type>. package MyTypes { use Type::Library -base; __PACKAGE__->add_type( name => 'EvenNumber', parent => Types::Standard::Int, constraint => sub { # in this sub we don't need to check that $_ is an Int # because the parent will take care of that! $_ % 2 == 0 }, ); } The C<add_type> method returns the type it just added, so it can be stored in a variable. my $Even = __PACKAGE__->add_type(...); This can be useful if you wish to use C<< $Even >> as the parent type to some other type you're going to define later. Here's a bigger worked example: package Example::Types { use Type::Library -base; use Types::Standard -types; use DateTime; # Type::Tiny::Class is a subclass of Type::Tiny for creating # InstanceOf-like types. It's kind of better though because # it does cool stuff like pass through $type->new(%args) to # the class's constructor. # my $dt = __PACKAGE__->add_type( Type::Tiny::Class->new( name => 'Datetime', class => 'DateTime', ) ); my $dth = __PACKAGE__->add_type( name => 'DatetimeHash', parent => Dict[ year => Int, month => Optional[ Int ], day => Optional[ Int ], hour => Optional[ Int ], minute => Optional[ Int ], second => Optional[ Int ], nanosecond => Optional[ Int ], time_zone => Optional[ Str ], ], ); my $eph = __PACKAGE__->add_type( name => 'EpochHash', parent => Dict[ epoch => Int ], ); # Can't just use "plus_coercions" method because that creates # a new anonymous child type to add the coercions to. We want # to add them to the type which exists in this library. # $dt->coercion->add_type_coercions( Int, q{ DateTime->from_epoch(epoch => $_) }, Undef, q{ DateTime->now() }, $dth, q{ DateTime->new(%$_) }, $eph, q{ DateTime->from_epoch(%$_) }, ); __PACKAGE__->make_immutable; } C<make_immutable> freezes to coercions of all the types in the package, so no outside code can tamper with the coercions, and allows Type::Tiny to make optimizations to the coercions, knowing they won't later be altered. You should always do this at the end. The library will export types B<Datetime>, B<DatetimeHash>, and B<EpochHash>. The B<Datetime> type will have coercions from B<Int>, B<Undef>, B<DatetimeHash>, and B<EpochHash>. =head2 Extending Libraries L<Type::Utils> provides a helpful function C<< extends >>. package My::Types { use Type::Library -base; use Type::Utils qw( extends ); BEGIN { extends("Types::Standard") }; # define your own types here } The C<extends> function (which you should usually use in a C<< BEGIN { } >> block not only loads another type library, but it also adds all the types from it to your library. This means code using the above My::Types doesn't need to do: use Types::Standard qw( Str ); use My::Types qw( Something ); It can just do: use My::Types qw( Str Something ); Because all the types from Types::Standard have been copied across into My::Types and are also available there. C<extends> can be passed a list of libraries; you can inherit from multiple existing libraries. It can also recognize and import types from L<MooseX::Types>, L<MouseX::Types>, and L<Specio::Exporter> libraries. Since Type::Library 1.012, there has been a shortcut for C<< extends >>. package My::Types { use Type::Library -extends => [ 'Types::Standard' ]; # define your own types here } The C<< -extends >> flag takes an arrayref of type libraries to extend. It automatically implies C<< -base >> so you don't need to use both. =head2 Custom Error Messages A type constraint can have custom error messages. It's pretty simple: Type::Tiny->new( name => 'EvenNumber', parent => Types::Standard::Int, constraint => sub { # in this sub we don't need to check that $_ is an Int # because the parent will take care of that! $_ % 2 == 0 }, message => sub { sprintf '%s is not an even number', Type::Tiny::_dd($_); }, ); The message coderef just takes a value in C<< $_ >> and returns a string. It may use C<< Type::Tiny::_dd() >> as a way of pretty-printing a value. (Don't be put off by the underscore in the function name. C<< _dd() >> is an officially supported part of Type::Tiny's API now.) You don't have to use C<< _dd() >>. You can generate any error string you like. But C<< _dd() >> will help you make undef and the empty string look different, and will pretty-print references, and so on. There's no need to supply an error message coderef unless you really want custom error messages. The default sub should be reasonable. =head2 Inlining In Perl, sub calls are relatively expensive in terms of memory and CPU use. The B<PositiveInt> type inherits from B<Int> which inherits from B<Num> which inherits from B<Str> which inherits from B<Defined> which inherits from B<Item> which inherits from B<Any>. So you might think that to check of C<< $value >> is a B<PositiveInt>, it needs to be checked all the way up the inheritance chain. But this is where one of Type::Tiny's big optimizations happens. Type::Tiny can glue together a bunch of checks with a stringy eval, and get a single coderef that can do all the checks in one go. This is why when Type::Tiny gives you a choice of using a coderef or a string of Perl code, you should usually choose the string of Perl code. A single coderef can "break the chain". But these automatically generated strings of Perl code are not always as efficient as they could be. For example, imagine that B<HashRef> is defined as: my $Defined = Type::Tiny->new( name => 'Defined', constraint => 'defined($_)', ); my $Ref = Type::Tiny->new( name => 'Ref', parent => $Defined, constraint => 'ref($_)', ); my $HashRef = Type::Tiny->new( name => 'HashRef', parent => $Ref, constraint => 'ref($_) eq "HASH"', ); Then the combined check is: defined($_) and ref($_) and ref($_) eq "HASH" Actually in practice it's even more complicated, because Type::Tiny needs to localize and set C<< $_ >> first. But in practice, the following should be a sufficient check: ref($_) eq "HASH" It is possible for the B<HashRef> type to have more control over the string of code generated. my $HashRef = Type::Tiny->new( name => 'HashRef', parent => $Ref, constraint => 'ref($_) eq "HASH"', inlined => sub { my $varname = pop; sprintf 'ref(%s) eq "HASH"', $varname; }, ); The inlined coderef gets passed the name of a variable to check. This could be C<< '$_' >> or C<< '$var' >> or C<< $some{deep}{thing}[0] >>. Because it is passed the name of a variable to check, instead of always checking C<< $_ >>, this enables very efficient checking for parameterized types. Although in this case, the inlining coderef is just returning a string, technically it returns a list of strings. If there's multiple strings, Type::Tiny will join them together in a big "&&" statement. As a special case, if the first item in the returned list of strings is undef, then Type::Tiny will substitute the parent type constraint's inlined string in its place. So an inlieing coderef for even numbers might be: Type::Tiny->new( name => 'EvenNumber', parent => Types::Standard::Int, constraint => sub { $_ % 2 == 0 }, inlined => sub { my $varname = pop; return (undef, "$varname % 2 == 0"); }, ); Even if you provide a coderef as a string, an inlining coderef has the potential to generate more efficient code, so you should consider providing one. =head2 Pre-Declaring Types use Type::Library -base, -declare => qw( Foo Bar Baz ); This declares types B<Foo>, B<Bar>, and B<Baz> at compile time so they can safely be used as barewords in your type library. This also allows recursively defined types to (mostly) work! use Type::Library -base, -declare => qw( NumericArrayRef ); use Types::Standard qw( Num ArrayRef ); __PACKAGE__->add_type( name => NumericArrayRef, parent => ArrayRef->of( Num | NumericArrayRef ), ); (Support for recursive type definitions added in Type::Library 1.009_000.) =head2 Parameterizable Types This is probably the most "meta" concept that is going to be covered. Building your own type constraint that can be parameterized like B<ArrayRef> or B<HasMethods>. The type constraint we'll build will be B<< MultipleOf[$i] >> which checks that an integer is a multiple of $i. __PACKAGE__->add_type( name => 'MultipleOf', parent => Int, # This coderef gets passed the contents of the square brackets. constraint_generator => sub { my $i = assert_Int(shift); # needs to return a coderef to use as a constraint for the # parameterized type return sub { $_ % $i == 0 }; }, # optional but recommended inline_generator => sub { my $i = shift; return sub { my $varname = pop; return (undef, "$varname % $i == 0"); }; }, # probably the most complex bit coercion_generator => sub { my $i = $_[2]; require Type::Coercion; return Type::Coercion->new( type_coercion_map => [ Num, qq{ int($i * int(\$_/$i)) } ], ); }, ); Now we can define an even number like this: __PACKAGE__->add_type( name => 'EvenNumber', parent => __PACKAGE__->get_type('MultipleOf')->of(2), coercion => 1, # inherit from parent ); Note that it is possible for a type constraint to have a C<constraint> I<and> a C<constraint_generator>. BaseType # uses the constraint BaseType[] # constraint_generator with no arguments BaseType[$x] # constraint_generator with an argument In the B<MultipleOf> example above, B<< MultipleOf[] >> with no number would throw an error because of C<< assert_Int(shift) >> not finding an integer. But it is certainly possible for B<< BaseType[] >> to be meaningful and distinct from C<< BaseType >>. For example, B<Tuple> is just the same as B<ArrayRef> and accepts any arrayref as being valid. But B<< Tuple[] >> will only accept arrayrefs with zero elements in them. (Just like B<< Tuple[Any,Any] >> will only accept arrayrefs with two elements.) =head1 NEXT STEPS After that last example, probably have a little lie down. Once you're recovered, here's your next step: =over =item * L<Type::Tiny::Manual::UsingWithMoose> How to use Type::Tiny with Moose, including the advantages of Type::Tiny over built-in type constraints, and Moose-specific features. =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 Save