Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

Exploring Type::Tiny Part 2: Using Type::Tiny with Moose

by tobyink (Canon)
on Aug 04, 2018 at 14:09 UTC ( [id://1219853]=CUFP: print w/replies, xml ) Need Help??

Type::Tiny is probably best known as a way of having Moose-like type constraints in Moo, but it can be used for so much more. This is the second in a series of posts showing other things you can use Type::Tiny for. Part 1 can be found at http://blogs.perl.org/users/toby_inkster/2018/07/exploring-typetiny-part-1-using-typeparams-for-validating-function-parameters.html.

Type::Tiny is often used in Moo classes and roles as a drop-in replacement for Moose's built-in type system. But the original reason I wrote it was as a response to the growing number of MooseX::Types and MouseX::Types modules on CPAN. I thought "wouldn't it be good if you could write a type library once, and use it for Moose, Mouse, and maybe even Moo?" In the very early version, you needed to import types like this:

use Type::Standard -moose, qw(Int); use Type::Standard -mouse, qw(Int); use Type::Standard -moo, qw(Int);

Specifying which object system you were using allowed the type library to export different blessed type constraint objects for different object frameworks. Eventually this need was eliminated by having Type::Tiny's objects better mock the Moose and Mouse native APIs, so the frameworks didn't even notice you weren't using their built-in type constraints.

(While no longer documented, the -moose, etc import flags still work in all Type::Library-based type libraries.)

Anyway, so now you know Type::Tiny types can work with Moose, what are the reasons to use them over Moose's built-in type constraints?

Type::Tiny is Faster

In almost all cases, Type::Tiny checks and coercions run faster than the built-in Moose ones.

use v5.16; use Benchmark qw(cmpthese); BEGIN { $ENV{PERL_TYPE_TINY_XS} = 0; } package Example::Native { use Moose; has numbers => ( is => 'rw', isa => 'ArrayRef[Str]', ); __PACKAGE__->meta->make_immutable; } package Example::TT { use Moose; use Types::Standard qw(ArrayRef Str); has numbers => ( is => 'rw', isa => ArrayRef[Str], ); __PACKAGE__->meta->make_immutable; } cmpthese -1, { native => q{ my $obj = Example::Native->new(numbers => []); $obj->numbers([0 .. $_]) for 1 .. 50; }, tt => q{ my $obj = Example::TT->new(numbers => []); $obj->numbers([0 .. $_]) for 1 .. 50; }, }; __END__ Rate native tt native 2511/s -- -45% tt 4525/s 80% --

Note that even without XS, the Type::Tiny checks run 80% faster than Moose's native ones. If Type::Tiny::XS is available, it's about 400% faster. (Yeah, I could have tested ArrayRef[Int] but sadly the Int type is one of the slower type checks in Types::Standard, no faster than Moose.)

Type::Tiny has a Better Coercion Paradigm

In Moose, if you want to, say, coerce an arrayref of strings into a single string, then the usual way to do it is something like this:

use Moose::Util::TypeConstraints; coerce 'Str', from 'ArrayRef', via { join "\n", @$_ };

However, this has a global effect. It doesn't just apply to string attributes in your class, but any string attributes which have coercion enabled for them.

While Type::Tiny does support globally defined coercions for Moose compatibility, the practice above, of adding your own coercions to types in standard libraries is strongly discouraged.

Instead, two routes to coercions are recommended.

Firstly, if you're making your own type library, feel free to define any useful coercions to the types in that library. Some of the type libraries bundled with Type::Tiny do include a few standard coercions. For example LowerCaseStr in Types::Common::String defines a coercion from non-empty strings (passing the string to Perl's lc function).

Secondly, if you're consuming types from a library (importing them into your role or class for use), don't add your own coercions to them. Instead, use the plus_coercions method.

package MyClass { use Moose; use Types::Standard qw(ArrayRef Str); has data => ( is => 'ro', isa => Str->plus_coercions(ArrayRef, sub { join "\n", @$_ +}), ); }

What does this do? Instead of adding coercions to the global definition of Str, it transparently creates a subtype of Str and adds your coercions to that.

There's also plus_fallback_coercions (which does the same thing but gives priority to any existing coercions the type constraint already has), minus_coercions (to remove particular existing coercions from a type), and no_coercions (to give you a blank slate).

Coercions can also be defined using strings of Perl code:

   Str->plus_coercions(ArrayRef, q{ join "\n", @$_ })

This allows them to be better optimized.

Type::Tiny Makes Subtyping a Breeze

package MyClass { use Moose; use Types::Standard qw(Int); has even_number => ( is => 'ro', isa => Int->where(sub { $_ % 2 == 0 }), ); }

Need I say more?

Probably not.

But I'll add that again, you can use a string of Perl code to get slightly better performance.

Type::Tiny and MooseX::Types Interoperate Fine

package MyClass { use Moose; use Types::Standard qw(ArrayRef); use MooseX::Types::Moose qw(Int); has will_this_work => ( is => 'ro', isa => ArrayRef[Int], ); }

Yeah, it works.

Validate Method Parameters

In part 1 of this series I described how you can use Type::Tiny type constraints to validate data passed to functions. If you're checking incoming data to your accessors and constructors, why not check parameters passed to method calls as well? Type::Params lets you use the same types and coercions you're familiar with from defining attributes to validate method parameters.

use v5.16; package MyClass { use Moose; use Types::Standard qw(Object Int); use Type::Params qw(compile); my $EvenInt = Int->where(sub { $_ % 2 == 0 }); has even_number => ( is => 'ro', isa => $EvenInt, writer => '_set_even_number', ); sub add_another_even { state $check = compile(Object, $EvenInt); my ($self, $n) = &$check; $self->_set_even_number( $self->even_number + $n ); return $self; } }

Replies are listed 'Best First'.
Re: Exploring Type::Tiny Part 2: Using Type::Tiny with Moose
by liz (Monsignor) on Aug 04, 2018 at 15:17 UTC

      No, but then this isn't copying an article. Rather this is an author publishing their article on two separate sites. Given the transience of some other sites it is quite possible that blogs.perl.org will go the way of use.perl.org at some point. By publishing in the Monastery the chances of the content being lost* over time are diminished.

      * Yes there is the wayback machine but does anyone browse that for old use.perl.org articles these days?

      Hi liz. Previously I've added links to the Perl News section to announce blogs.perl posts, but I was not the author of any of the linked posts. Posting here could arguably reach people who don't follow the blogs.perl site, and also has a benefit in so much that with Anonymous Monk the barrier for participation is lower, should anyone which to discuss anything relating to the post they don't have to be registered anywhere.

      Why? We are not really overwhelmed with all those other interesting meditations here.

      Though maybe he could add cross links.

      - LanX

      G'day liz,

      As AM Update: "the referenced node has been reassigned to LanX" - PM from erzuuli (signed "-LanX") said, a cross-link might have been useful. So, just for the record, here's the PM Part1: "Exploring Type::Tiny Part 1: Using Type::Params for Validating Function Parameters".

      I had a look at the timestamps on both postings. They're very close. Given timezone differences (and possibly other factors) I can't tell which was posted first; I'm seeing roughly 9 hours difference between the two, I'm UTC+10.00, I've no idea of the "blogs" server's timezone.

      — Ken

        Imho both should be cross linked, for pretty much the same reason like with questions:

        A reader might want to see the discussions elsewhere before reinventing a "wheel" of replies.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

        Nine hours? Nah, more like ten minutes. Blogs.perl.org first, then dumbed down the HTML for Perlmonks.

      *That's* your feedback on a well-written, informative tutorial on a powerful, extremely *useful* and widely-*used* piece of Perl infrastructure?

      Toby, please keep posting despite the negative nellies, butterfly boosters and pedants who have not contributed a fraction of what you have to modern Perl development.

      The way forward always starts with a minimal test.
        OOC, is it nowadays common to copy an article from blogs,perl.org verbatim to PerlMonks? Instead of just posting a link in news or somesuch?

        I was just curious (Out Of Curiosity!) if that was a thing to do or not on PerlMonks. I haven't really been participating in PerlMonks for the longest time. And from what I remembered, copy-pasting was at least at one time something that was less appreciated.

        I did not say anything about the article (which I think is very well written, that's why I +-ed it), I just had a question about the process.

        I however do resent being described as a negative nelly or as a pedant. I don't think I deserve that.

        So please stop doing that.

        A reply falls below the community's threshold of quality. You may see it by logging in.
        *That's* your reaction to a neutral question? You saw some criticism where there was none. You are using negative words, and insults, to somebody who has done a load of support to Perl in general, both Perl 5 and Perl 6.
        A reply falls below the community's threshold of quality. You may see it by logging in.
        Ad hominem, as usual. :(

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://1219853]
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (7)
As of 2024-04-16 08:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found