The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=pod

=for stopwords smush smushed

=encoding utf-8

=head1 NAME

Type::Tiny::Manual::Libraries - how to build a type library with Type::Tiny, Type::Library and Type::Utils

=head1 SYNOPSIS

A type library is a collection of type constraints, optionally with coercions.

The following is an example type library:

   package Types::Datetime;
   
   use Type::Library
      -base,
      -declare => qw( Datetime DatetimeHash EpochHash );
   use Type::Utils -all;
   use Types::Standard -types;
   
   class_type Datetime, { class => "DateTime" };
   
   declare DatetimeHash,
      as 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 ],
      ];
   
   declare EpochHash,
      as Dict[ epoch => Int ];
   
   coerce Datetime,
      from Int,          via { "DateTime"->from_epoch(epoch => $_) },
      from Undef,        via { "DateTime"->now },
      from DatetimeHash, via { "DateTime"->new(%$_) },
      from EpochHash,    via { "DateTime"->from_epoch(%$_) };
   
   1;

=head1 DESCRIPTION

Here's a line by line description of what's going on in the type library.

   package Types::Datetime;

Type libraries are packages. It is recommended that re-usable type libraries
be given a name in the C<< Types::* >> namespace. For application-specific
type libraries, assuming your application's namespace is C<< MyApp::* >> then
name the type library C<< MyApp::Types >>, or if more than one is needed, use
the C<< MyApp::Types::* >> namespace.

   use Type::Library
      -base,
      -declare => qw( Datetime DatetimeHash EpochHash );

The C<< -base >> part is used to establish inheritance. It makes
C<Types::Datetime> a child class of C<Type::Library>.

Declaring the types we're going to define ahead of their definition allows
us to use them as barewords later on. (Note that in code which I<uses>
our type library, the types will always be available as barewords. The
declaration above just allows us to use them within the library itself.)

   use Type::Utils -all;

Imports some utility functions from L<Type::Utils>. These will be useful
for defining our types and the relationships between them.

   use Types::Standard -types;

Here we import a standard set of type constraints from L<Types::Standard>.
There is no need to do this, but it's often helpful to have a base set of
types which we can define our own in terms of.

Note that although we've imported the types to be able to use in our library,
we haven't I<added> the types to our library. We've imported C<Str>, but other
people won't be able to re-import C<Str> from our library. If you actually
want your library to I<extend> another library, do this instead:

   BEGIN { extends "Types::AnotherLibrary" };

(Note: if your code breaks here when you upgrade from version 0.006 or
below, saying that the 'extends' keyword has not been declared, just add
'-all' after use Type::Utils.)

OK, now we're ready to declare a few types.

   class_type Datetime, { class => "DateTime" };

This creates a type constraint named "Datetime" which is all objects blessed
into the L<DateTime> package. Because this type constraint is not anonymous
(it has a name), it will be automagically installed into the type library.

The next two statements declare two further types constraints, using type
constraints from the Types::Standard library. Let's look at C<EpochHash> in
more detail. This is a hashref with one key called "epoch" and a value which
is an integer.

   declare EpochHash,
      as Dict[ epoch => Int ];

C<EpochHash> inherits from the C<Dict> type defined in Types::Standard. It
equally could have been defined as:

   declare EpochHash,
      as HashRef[Int],
      where { scalar(keys(%$_))==1 and exists $_->{epoch} };

Or even:

   declare EpochHash,
      where {
             ref($_) eq "HASH"
         and scalar(keys(%$_))==1
         and exists $_->{epoch}
      };

Lastly we set up coercions. It's best to define all your types before you
define any coercions.

   coerce Datetime,
      from Int,          via { "DateTime"->from_epoch(epoch => $_) },
      from Undef,        via { "DateTime"->now },
      from DatetimeHash, via { "DateTime"->new(%$_) },
      from EpochHash,    via { "DateTime"->from_epoch(%$_) };

These are simply coderefs that will be fired when you want a Datetime,
but are given something else. For more information on coercions, see
L<Type::Tiny::Manual::Coercions>.

=head1 ADVANCED TOPICS

=head2 Messages

It is sometimes nice to be able to emit a more useful error message than
the standard:

   Value "Foo" did not pass type constraint "Bar"

It is possible to define custom error messages for types.

   declare MediumInteger, as Integer,
      where   { $_ >= 10 and $_ < 20 },
      message {
         return Integer->get_message($_) if !Integer->check($_);
         return "$_ is too small!"       if $_ < 10;
         return "$_ is so very, very big!";
      };

=head2 Inlining

If your type constraint can be inlined, this can not only speed up
Type::Tiny's own checks and coercions, it may also allow your type constraint
to be inlined into generated methods such as Moose attribute accessors.

All of the constraints from C<Types::Standard> can be inlined, as can enum,
class_type, role_type and duck_type constraints. Union and intersection
constraints can be inlined if their sub-constraints can be. So if you can
define your own types purely in terms of these types, you automatically
get inlining:

   declare HashLike, as union [
      Ref["HASH"],
      Overload["&{}"],
   ];

However, sometimes these base types are not powerful enough and you'll need
to write a constraint coderef:

   declare NonEmptyHash, as HashLike,
      where     { scalar values %$_ };

... and you've suddenly sacrificed a lot of speed.

Inlining to the rescue! You can define an inlining coderef which will be
passed two parameters: the constraint itself and a variable name as a string.
For example, the variable name might be C<< '$_' >> or C<< '$_[0]' >>.
Your coderef should return a Perl expression string, interpolating that
variable name.

   declare NonEmptyHash, as HashLike,
      where     { scalar values %$_ },
      inline_as {
         my ($constraint, $varname) = @_;
         return sprintf(
            '%s and scalar values %%{%s}',
            $constraint->parent->inline_check($varname),
            $varname,
         );
      };

The Perl expression could be inlined within a function or a C<if> clause or
potentially anywhere, so it really must be an expression, not a statement.
It should not C<return> or C<exit> and probably shouldn't C<die>. (If you
need loops and so on, you can output a C<do> block.)

Note that if you're subtyping an existing type constraint, your C<inline_as>
block is also responsible for checking the parent type's constraint. This
can be done quite easily, as shown in the example above.

Note that defining a type constraint in terms of a constraint coderef and an
inlining coderef can be a little repetitive. L<Sub::Quote> provides an
alternative that reduces repetition (though the inlined code might not be as
compact/good/fast).

   declare NonEmptyHash, as HashLike,
      constraint => quote_sub q{ scalar values %$_ };

Aside: it's been pointed out that "might not be as fast" above is a bit
hand-wavy. When Type::Tiny does inlining from Sub::Quote coderefs, it needs
to inline all the ancestor type constraints, and smush them together with
C<< && >>. This may result in duplicate checks. For example, if 'MyArray'
inherits from 'MyRef' which inherits from 'MyDef', the inlined code might
end up as:

   defined($_)              # check MyDef
   && ref($_)               # check MyRef
   && ref($_) eq 'ARRAY'    # check MyArray

When just the last check would have been sufficient. A custom C<inline_as>
allows you finer control over how the type constraint is inlined.

=head2 Parameterized Constraints

Parameterized type constraints are those that can generate simple child type
constraints by passing parameters to their C<parameterize> method. For
example, ArrayRef in Types::Standard:

   use Types::Standard;
   
   my $ArrayRef         = Types::Standard::ArrayRef;
   my $Int              = Types::Standard::Int;
   my $ArrayRef_of_Ints = $ArrayRef->parameterize($Int);

Type libraries provide some convenient sugar for this:

   use Types::Standard qw( ArrayRef Int );
   
   my $ArrayRef_of_Ints = ArrayRef[Int];

Unlike L<Moose> which has separate meta classes for parameterizable,
parameterized and non-parameterizable type constraints, L<Type::Tiny> handles
all that in one.

To create a parameterizable type constraint, you'll need to pass an extra
named parameter to C<declare>. Let's imagine that we want to make our earlier
C<NonEmptyHash> constraint accept a parameter telling it the minimum size of
the hash. For example C<< NonEmptyHash[4] >> would need to contain at least
four key-value pairs. Here's how you'd do it:

   declare NonEmptyHash, as HashLike,
      where     { scalar values %$_ },
      inline_as {
         my ($constraint, $varname) = @_;
         return sprintf(
            '%s and scalar values %%{%s}',
            $constraint->parent->inline_check($varname),
            $varname,
         );
      },
      # Generate a new "where" coderef...
      constraint_generator => sub {
         my ($minimum) = @_;
         die "parameter must be positive" unless int($minimum) > 0;
         return sub {
             scalar(values(%$_)) >= int($minimum);
         };
      },
      # Generate a new "inline_as" coderef...
      inline_generator => sub {
         my ($minimum) = @_;
         return sub {
            my ($constraint, $varname) = @_;
            return sprintf(
               '%s and scalar(values(%%{%s})) >= %d',
               $constraint->parent->inline_check($varname),
               $varname,
               $minimum,
            );
         };
      };

=head1 SEE ALSO

Some type libraries on CPAN:

=over

=item *

L<Types::Standard>

=item *

L<Types::Encodings>

=item *

L<Types::Path::Tiny>

=item *

L<Unexpected::Types>

=item *

L<Geo::JSON::Types>

=item *

L<Types::XSD> / L<Types::XSD::Lite>

=item *

L<Types::Set>

=back

=head1 AUTHOR

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

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2013 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