Acme-oop-ism Part Two: Type::Tiny

Acme-oop-ism is about writing code that works in Moose, Mouse and Moo.

Type::Tiny was born of frustration with how MooX::Types::MooseLike handles "inflation". Inflation is how Moo handles interacting with Moose. I'm simplifying here, but when Moo detects that Moose is being used, it builds a Moose::Meta::Class for each Moo class you've defined, and a Moose::Meta::Role for each role.

MooX::Types::MooseLike hooks into the inflation system so that if a Moo attribute uses, say, the PositiveInt type constraint from MooX::Types::MooseLike::Numeric, this gets inflated to the PositiveInt type from MooseX::Types::Common::Numeric.

After seeing a bug report from the Dancer2 developers whose type constraints were breaking during inflation, I started thinking about how this system was somewhat fragile and repetitive.

Let's rewind a bit; what's a type constraint? In Moose, Mouse and Moo classes, you can say that a certain class attribute "isa" string or a number or an integer, using syntax along these lines:

 has favourite_number => (isa => Int);

Int is a type constraint, defined in a library somewhere, something like this:

 type("Int" => sub { $_ =~ m/\A-?[0-9]+\z/s });

So, type constraints are basically just coderefs that return true or false after testing $_ against some rule. If we've got a list of these coderefs, why can't we use that list to generate both a Moo type constraint library and a Moose one?

And so Type::Tiny was born. It started off as above. You'd create a library of coderefs in MyTypes.pm. Then, from your Moo classes, you'd import use MyTypes -moo; from Moose classes, use MyTypes -moose and from Mouse classes... well, I'm sure you've already guessed.

No need for MouseX::Types::URI and MooseX::Types::URI to remain separate any more; MooseX::Types::DateTime and MouseX::Types::DateTime could be united at last; MouseX::Types::Path::Class and MooseX::Types::Path::Class could live together in harmony. (Note: none of these type constraint libraries has taken the plunge... yet.)

By the time Type::Tiny 0.004 was released, by overloading &{} and by carefully mocking the Moose::Meta::TypeConstraint and Mouse::Meta::TypeConstraint APIs, even the need for these little -moo, -moose and -mouse import flags was gone.

So Type::Tiny works fairly transparently with all three of the major modern Perl OO frameworks, yet depends on none of them. (Making it useful outside OO code too.) There are no doubt still some bugs to be worked out, but it demonstrates that it's possible.

What other features could potentially be built to work cross-framework? Extensions for singleton classes? Or abstract classes?

In the next part, we'll look at some of the tricks of the trade.

1 Comment

This is wonderful, excellent news. Thanks so much! (found via Perl Weekly)

Leave a comment

About Toby Inkster

user-pic I'm tobyink on CPAN, IRC and PerlMonks.