=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<is>.

  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<Int> 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<HashRef> 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<Int> 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<store_cookie> method. Perhaps C<store_cookie> 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<Foo> 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<things> is now an arrayref of anything except coderefs.

If you need a number that is I<not> an integer:

  Num & ~Int

L<Types::Standard> includes two types which are complements of each other:
B<Undef> and B<Defined>.

B<NegativeInt> might seem to be the complement of B<PositiveOrZeroInt> 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<stringifies_to> and C<numifies_to>

The B<Object> type constraint provides C<stringifies_to> and C<numifies_to>
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<HasMethods>, B<InstanceOf>, and B<ConsumerOf> should also provide
C<stringifies_to> and C<numifies_to> methods.

C<stringifies_to> and C<numifies_to> work on unions if I<all> of the
type constraints in the union offer the method.

C<stringifies_to> and C<numifies_to> work on intersections if
I<at least one of> of the type constraints in the intersection offers the
method.

=head2 C<with_attribute_values>

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<stringifies_as> and C<numifies_as>,
so like those, it only applies to B<Object> 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<stringifies_as>, C<numifies_as>, and C<with_attributes_as> are
really just wrappers around C<where>. 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<Types::Self> module on CPAN that will define a B<Self>
type meaning B<< InstanceOf[ __PACKAGE__ ] >> for you!)

=head1 NEXT STEPS

Here's your next step:

=over

=item * L<Type::Tiny::Manual::UsingWithMoo3>

There's more than one way to do it! Alternative ways of using Type::Tiny,
including type registries, exported functions, and C<dwim_type>.

=back

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=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

