=pod =encoding utf-8 =head1 NAME Type::Tiny::Manual::UsingWithMoo2 - advanced use of Type::Tiny with Moo =head1 MANUAL =head2 What is a Type? So far all the examples have shown you how to work with types, but we haven't looked at what a type actually I. use Types::Standard qw( Int ); my $type = Int; C<< Int >> in the above code is just a function called with zero arguments which returns a blessed Perl object. It is this object that defines what the B type is and is responsible for checking values meet its definition. use Types::Standard qw( HashRef Int ); my $type = HashRef[Int]; The C<< HashRef >> function, if called with no parameters returns the object defining the B type, just like the C<< Int >> function did before. But the difference here is that it's called with a parameter, an arrayref containing the B type object. It uses this to make the B<< HashRef[Int] >> type and returns that. Like any object, you can call methods on it. The most important methods to know about are: # check the value and return a boolean # $type->check($value); # return an error message about $value failing the type check # but don't actually check the value # $type->get_message($value); # coerce the value # my $coerced = $type->coerce($value); We've already seen some other methods earlier in the tutorial. # create a new type, same as the old type, but that has coercions # my $new_type = $type->plus_coercions( ... ); # different syntax for parameterized types # my $href = HashRef; my $int = Int; my $href_of_int = $href->of($int); So now you should understand this: use Types::Standard qw( ArrayRef Dict Optional ); use Types::Common::Numeric qw( PositiveInt ); use Types::Common::String qw( NonEmptyStr ); my $RaceInfo = Dict[ year => PositiveInt, race => NonEmptyStr, jockey => Optional[NonEmptyStr], ]; has latest_event => ( is => 'rw', isa => $RaceInfo ); has wins => ( is => 'rw', isa => ArrayRef[$RaceInfo] ); has losses => ( is => 'rw', isa => ArrayRef[$RaceInfo] ); This can help you avoid repetition if you have a complex parameterized type that you need to reuse a few times. =head2 C<< where >> One of the most useful methods you can call on a type object is C<< where >>. use Types::Standard qw( Int ); has lucky_number => ( is => 'ro', isa => Int->where(sub { $_ != 13 }), ); I think you already understand what it does. It creates a new type constraint on the fly, restricting the original type. Like with coercions, these restrictions can be expressed as a coderef or as a string of Perl code, operating on the C<< $_ >> variable. And like with coercions, using a string of code will result in better performance. use Types::Standard qw( Int ); has lucky_number => ( is => 'ro', isa => Int->where(q{ $_ != 13 }), ); Let's coerce a hashref of strings from an even-sized arrayref of strings: use Types::Standard qw( HashRef ArrayRef Str ); has stringhash => ( is => 'ro', isa => HashRef->of(Str)->plus_coercions( ArrayRef->of(Str)->where(q{ @$_ % 2 == 0 }), q{ my %h = @$_; \%h; }, ), coerce => 1, # never forget! ); If you understand that, you really are in the advanced class. Congratulations! =head2 Unions Sometimes you want to accept one thing or another thing. This is pretty easy with Type::Tiny. use Types::Standard qw( HashRef ArrayRef Str ); has strings => ( is => 'ro', isa => ArrayRef[Str] | HashRef[Str], ); Type::Tiny overloads the bitwise or operator so stuff like this should "just work". That said, now any code that calls C<< $self->strings >> will probably need to check if the value is an arrayref or a hashref before doing anything with it. So it may be simpler overall if you just choose one of the options and coerce the other one into it. =head2 Intersections Similar to a union is an intersection. package MyAPI::Client { use Moo; use Types::Standard qw( HasMethods InstanceOf ); has ua => ( is => 'ro', isa => (InstanceOf["MyUA"]) & (HasMethods["store_cookie"]), ); } Here we are checking that the UA is an instance of the MyUA class and also offers the C method. Perhaps C isn't provided by the MyUA class itself, but several subclasses of MyUA provide it. Intersections are not useful as often as unions are. This is because they often make no sense. C<< (ArrayRef) & (HashRef) >> would be a reference which was simultaneously pointing to an array and a hash, which is impossible. Note that when using intersections, it is good practice to put parentheses around each type. This is to disambiguate the meaning of C<< & >> for Perl, because Perl uses it as the bitwise and operator but also as the sigil for subs. =head2 Complements For any type B there is a complementary type B<< ~Foo >> (pronounced "not Foo"). package My::Class { use Moo; use Types::Standard qw( ArrayRef CodeRef ); has things => ( is => 'ro', isa => ArrayRef[~CodeRef] ); } C is now an arrayref of anything except coderefs. If you need a number that is I an integer: Num & ~Int L includes two types which are complements of each other: B and B. B might seem to be the complement of B but when you think about it, it is not. There are values that fall into neither category, such as non-integers, non-numeric strings, references, undef, etc. =head2 C and C The B type constraint provides C and C methods which are probably best explained by examples. C<< Object->numifies_to(Int) >> means any object where C<< 0 + $object >> is an integer. C<< Object->stringifies_to(StrMatch[$re]) >> means any object where C<< "$object" >> matches the regular expression. C<< Object->stringifies_to($re) >> also works as a shortcut. C<< Object->numifies_to($coderef) >> and C<< Object->stringifies_to($coderef) >> also work, where the coderef checks C<< $_ >> and returns a boolean. Other types which are also logically objects, such as parameterized B, B, and B should also provide C and C methods. C and C work on unions if I of the type constraints in the union offer the method. C and C work on intersections if I of the type constraints in the intersection offers the method. =head2 C Another one that is probably best explained using an example: package Horse { use Types::Standard qw( Enum Object ); has gender => ( is => 'ro', isa => Enum['m', 'f'], ); has father => ( is => 'ro', isa => Object->with_attribute_values(gender => Enum['m']), ); has mother => ( is => 'ro', isa => Object->with_attribute_values(gender => Enum['f']), ); } In this example when you set a horse's father, it will call C<< $father->gender >> and check that it matches B<< Enum['m'] >>. This method is in the same family as C and C, so like those, it only applies to B and similar type constraints, can work on unions/intersections under the same circumstances, and will also accept coderefs and regexps. has father => ( is => 'ro', isa => Object->with_attribute_values(gender => sub { $_ eq 'm' }), ); has mother => ( is => 'ro', isa => Object->with_attribute_values(gender => qr/^f/i), ); All of C, C, and C are really just wrappers around C. The following two are roughly equivalent: my $type1 = Object->with_attribute_values(foo => Int, bar => Num); my $type2 = Object->where(sub { Int->check( $_->foo ) and Num->check( $_->bar ) }); The first will result in better performing code though. =head2 Tied Variables It is possible to tie variables to a type constraint. use Types::Standard qw(Int); tie my $n, Int, 4; print "$n\n"; # says "4" $n = 5; # ok $n = "foo"; # dies You can also tie arrays: tie my @numbers, Int; push @numbers, 1 .. 10; And hashes: tie my %numbers, Int; $numbers{lucky} = 7; $numbers{unlucky} = 13; Earlier in the manual, it was mentioned that there is a problem with code like this: push @{ $horse->children }, $non_horse; This can be solved using tied variables. tie @{ $horse->children }, InstanceOf["Horse"]; Here is a longer example using builders and triggers. package Horse { use Moo; use Types::Standard qw( Str Num ArrayRef InstanceOf ); use Type::Params qw( signature ); use namespace::autoclean; my $ThisClass = InstanceOf[ __PACKAGE__ ]; has name => ( is => 'ro', isa => Str ); has gender => ( is => 'ro', isa => Str ); has age => ( is => 'rw', isa => Num ); has children => ( is => 'rw', isa => ArrayRef[$ThisClass], builder => "_build_children", trigger => sub { shift->_trigger_children(@_) }, ); # tie a default arrayref sub _build_children { my $self = shift; tie my @kids, $ThisClass; \@kids; } # this method will tie an arrayref provided by the caller sub _trigger_children { my $self = shift; my ($new) = @_; tie @$new, $ThisClass; } sub add_child { state $check = signature( method => $ThisClass, positional => [ $ThisClass ], ); my ( $self, $kid ) = &$check; push @{ $self->children }, $kid; return $self; } } Now it's pretty much impossible for the caller to make a mess by adding a non-horse as a child. (Note there's a L module on CPAN that will define a B type meaning B<< InstanceOf[ __PACKAGE__ ] >> for you!) =head1 NEXT STEPS Here's your next step: =over =item * L There's more than one way to do it! Alternative ways of using Type::Tiny, including type registries, exported functions, and C. =back =head1 AUTHOR Toby Inkster Etobyink@cpan.orgE. =head1 COPYRIGHT AND LICENCE This software is copyright (c) 2013-2014, 2017-2023 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