#!/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