Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Type::Tiny v2 is Coming

by tobyink (Canon)
on Sep 16, 2022 at 15:19 UTC ( [id://11146915]=CUFP: print w/replies, xml ) Need Help??

Eagle-eyed watchers of CPAN may have noticed that I've recently been releasing Type::Tiny development releases with version numbers 1.999_XYZ.

Type::Tiny v2 is intended to be compatible with Type::Tiny v1. If you've used Type::Tiny v1, you shouldn't need to change any code, but Type::Tiny v2 has a few new features which may make your code simpler, more maintainable, and more readable if you adopt them.

Type::Params v2 API

Type::Params can be used to provide typed subroutine signatures:

use feature qw( state ); use Type::Params qw( compile ); use Types::Standard qw( Num ); sub add_numbers { state $signature = compile( Num, Num ); my ( $x, $y ) = $signature->( @_ ); return $x + $y; }

However, things like named paramaters, catering for $self in methods, etc felt like afterthoughts. Here is how you'd write the same signature in version 1 as a method call using named parameters:

use feature qw( state ); use Type::Params qw( compile_named_oo ); use Types::Standard qw( Num ); sub add_numbers { state $signature = compile_named_oo( { head => [ Any ] }, 'x' => Num, 'y' => Num, ); my ( $self, $arg ) = $signature->( @_ ); return $arg->x + $arg->y; }

While the old API is still supported, Type::Params v2 has two new functions, signature and signature_for, which I feel provide a more powerful and more consistent interface.

signature works much the same as compile, but takes a top-level hash of options, allowing it to cater for both positional and named parameters.

Here is an example for positional parameters:

use feature qw( state ); use Type::Params qw( signature ); use Types::Standard qw( Num ); sub add_numbers { state $signature = signature( method => 0, positional => [ Num, Num ], ); my ( $x, $y ) = $signature->( @_ ); return $x + $y; }

Here is an example for named parameters:

use feature qw( state ); use Type::Params qw( signature ); use Types::Standard qw( Num ); sub add_numbers { state $signature = signature( method => 1, named => [ 'x' => Num, 'y' => Num ], ); my ( $self, $arg ) = $signature->( @_ ); return $arg->x + $arg->y; }

And signature_for allows you to turn that definition inside-out.

use experimental qw( signatures ); use Type::Params qw( signature_for ); use Types::Standard qw( Num ); signature_for add_numbers => ( method => 1, named => [ 'x' => Num, 'y' => Num ], ); sub add_numbers ( $self, $arg ) { return $arg->x + $arg->y; }

Handy import shortcuts

A handy way to define an Enum type in Type::Tiny 2 is:

use Type::Tiny::Enum Size => [ qw( S M L XL ) ];

You can use this in a class like:

package Local::TShirt { use Moose; use Types::Common -types; use Type::Tiny::Enum Size => [ qw( S M L XL ) ]; use namespace::autoclean; has size => ( is => 'ro', isa => Size, required => 1, ); sub price { my $self = shift; my $size = $self->size; if ( $size eq SIZE_XL ) { return 10.99; } elsif ( $size eq SIZE_L ) { return 9.99; } else { return 8.99; } } }

Yes, Enum type constraints now provide constants like SIZE_XL above.

Type::Tiny::Class provides a similar shortcut:

sub post_data ( $url, $data, $ua=undef ) { use Type::Tiny::Class -lexical, 'HTTP::Tiny'; $ua = HTTPTiny->new unless is_HTTPTiny $ua; $ua->post( $url, $data ); }

Type::Tiny::Role and Type::Tiny::Duck also provide shortcuts.

Types::Common

Having checked out a lot of modules which use Type::Tiny, I've noticed that the most common modules people import from are Types::Standard, Type::Params, Types::Common::Numeric, and Types::Common::String.

Types::Common is a new module that combines all of the above. For quick scripts and one-liners, something like this may save a bit of typing:

use Types::Common -all;

Though like always, you can list imports explicitly:

use Types::Common qw( signature_for Num NonEmptyStr ); </pre> <p>If you have a bleeding-edge Perl installed, you can import function +s lexically:</p> <c> use Types::Common -lexical, -all;

A type divided against itself shall stand

You can now divide a type constraint by another:

has lucky_numbers => ( is => 'ro', isa => ArrayRef[ Num / Any ], );

What does this mean?

Under normal circumstances, Num/Any evaluates to just Any. Num is basically just documentation, so you're documenting that lucky_numbers is intended to be an arrayref of numbers, but as a speed boost, the attribute will just check that it's an arrayref of anything.

When the EXTENDED_TESTING environment variable is switched on though, Num/Any will evaluate to Num, so stricter type checks will kick in.

Type defaults

Instead of this:

has output_list => ( is => 'ro', isa => ArrayRef, default => sub { [] }, );

You can now write this:

has output_list => ( is => 'ro', isa => ArrayRef, default => ArrayRef->type_default, );

This is more typing, so why do this? Well, for ArrayRef it might be more typing, but in this case:

has colour_scheme => ( is => 'ro', isa => ColourScheme, default => sub { my %colours = ( foreground => 'black', background => 'white', links => 'blue', highlight => 'red', ); return \%colours; }, );

It might be neater to include the default in the definition of your ColourScheme type.

The new DelimitedStr type

Types::Common::String now has a DelimitedStr type.

This allows DelimitedStr[ "|", Int ] to accept strings like "12|34|-99|0|1".

Internals

There have been numerous internal refactorings in Type::Tiny v2, so if you're using Type::Tiny and its related modules in more unorthodox ways, it may be worth explicitly testing your code still runs on the new version.

However, I have taken care to avoid breaking any documented APIs. The vast majority of the Type:Tiny v1 test suite still passes with Type::Tiny v2, with test cases that inspect the exact text of error messages being the only real change.

Log In?
Username:
Password:

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

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

    No recent polls found