#!/usr/bin/env perl
use TOBYINK::Pod::HTML;
print "TOBYINK::Pod::HTML"->new(
pretty => 1,
code_highlighting => 1,
code_line_numbers => 0,
)->file_to_html(__FILE__);
=pod
=head1 Introducing Type::Tiny
L is a tiny (no non-core dependencies) framework for building
type constraints. OK, probably not that exciting. How can I grab your
attention?
=for highlighter language=Text
Rate WithMoose WithMooseAndTypeTiny
WithMoose 8071/s -- -25%
WithMooseAndTypeTiny 10778/s 34% --
=for highlighter language=Perl
The benchmark script is shown later so you can check I'm not doing anything
hideously unfair to disadvantage Moose.
How can I I your attention?
B<< Type constraint libraries built with Type::Tiny work with Moose, Mouse
I Moo! >> And because it's such a lightweight framework, with no
dependencies on heavy metaobject protocols, it even becomes appealing to use
in situations where you might not otherwise consider using a type constraint
library at all.
Type::Tiny comes bundled with a number of other modules that help round out
the framework, including:
=over
=item *
L - a base class for collections of type constraints
=item *
L - syntactic sugar for declaring types
=item *
L - a library of commonly used types
=item *
L - simple functions for testing type constraints work
=back
=head2 Type Constraints?
Let's get back to basics... what's a type constraint library? If you're
writing anything more than a quick throwaway script, you generally need to
do a bit of data validation. Your C function might need to check
that it gets passed an arrayref and all the values in the array are numeric.
In another part of the code your C function might
also need to accept an array of numeric values. Two checks for arrayrefs
of numbers, in different parts of your codebase. The principle of
L says that you
should factor both of these checks out to a single place in your code.
Once you've factored all of these checks out into one place, that's your
type constraint library.
=head2 Building a Type Library with Type::Library
Let's say we want to build a "natural numbers" type constraint. Natural
numbers are the positive integers plus zero. (The inclusion of zero is
contentious in some circles, but we'll put that aside for now.) It helps
that L defines an C type constraint, so rather than
starting from scratch, we can refine that.
package MyApp::Types;
use base "Type::Library"; # type libraries must inherit from this
use Type::Utils; # sugar for declaring type constraints
use Types::Standard qw(Int);
declare "NaturalNum", as Int, where { $_ >= 0 };
1; # magic true value
That was easy. Now within our application we can:
use MyApp::Types qw(NaturalNum);
And this will export C as a "constant". The constant returns
an object that we can call methods on, so:
NaturalNum->check($value); # returns true or false
NaturalNum->assert_valid($value); # returns true or dies
The constant can also be used directly within L or L attribute
declarations:
has message_count => (is => "ro", isa => NaturalNum, required => 1);
=head2 Coercions
A next step is to define coercions. Within our type constraint library we
can add:
use Types::Standard qw( Num ArrayRef );
coerce "NaturalNum",
from Num, via { int(abs($_)) },
from ArrayRef, via { scalar(@$_) };
Now within our application we can:
use MyApp::Types qw(to_NaturalNum);
my $goats = ["Alice Gruff", "Bob Gruff", "Carol Gruff"];
say to_NaturalNum($goats); # say 3
Coercions can be used within Moose attribute definitions:
has message_count => (
is => "ro",
isa => NaturalNum,
required => 1,
coerce => 1,
);
Or Moo attribute definitions:
has message_count => (
is => "ro",
isa => NaturalNum,
required => 1,
coerce => NaturalNum->coercion, # spot the difference
);
Coercions are a useful feature, and there are planned additions to
Type::Coercion and Type::Library to make them even better in the future.
* * *
Anyway, I hope this provides a brief summary of Type::Tiny's features,
and maybe tempts you to try it out. Keep an eye out for future articles on
topics such as optimizing type constraints, and coercion power features.
=head2 Appendix
Here's the benchmarking script as promised...
package main;
use strict;
use warnings;
use Benchmark qw(cmpthese);
{
package Class::WithMoose;
use Moose;
has attr => (is => "ro", isa => "ArrayRef[Int]");
__PACKAGE__->meta->make_immutable;
}
{
package Class::WithMooseAndTypeTiny;
use Moose;
use Types::Standard -all;
has attr => (is => "ro", isa => ArrayRef[Int]);
__PACKAGE__->meta->make_immutable;
}
our %data = ( attr => [1 .. 20] );
cmpthese(-1, {
WithMoose => q{ Class::WithMoose->new(%::data) },
WithMooseAndTypeTiny => q{ Class::WithMooseAndTypeTiny->new(%::data) },
});
=cut