=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