=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 ( $i ) {
      assert_Int $i;
      # needs to return a coderef to use as a constraint for the
      # parameterized type
      return sub { $_ % $i == 0 };
    },
    
    # optional but recommended
    inline_generator => sub ( $i ) {
      return sub {
        my $varname = pop;
        return (undef, "$varname % $i == 0");
      };
    },
    
    # probably the most complex bit
    coercion_generator => sub ( $parent_type, $child_type, $i ) {
      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-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

