Beefy Boxes and Bandwidth Generously Provided by pair Networks
Don't ask to ask, just ask
 
PerlMonks  

comment on

( [id://3333] : superdoc . 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 fifth in a series of posts showing other things you can use Type::Tiny for. This article along with the earlier ones in the series can be found on my blog and in the Cool Uses for Perl section of PerlMonks.

It's pretty common to do things like this:

use Types::Standard qw( is_ArrayRef is_HashRef ); use Carp qw( croak ); sub process_data { my ($self, $data) = @_; if (is_ArrayRef($data)) { $self->_process_value($_) for @$data; } elsif (is_HashRef($data)) { $self->_process_value($_) for values %$data; } else { croak "Could not grok data"; } }

Type::Utils provides a perhaps slightly neater way to do this:

use Types::Standard qw( ArrayRef HashRef Any ); use Type::Utils qw( match_on_type ); use Carp qw( croak ); sub process_data { my ($self, $data) = @_; match_on_type $data, ArrayRef, sub { $self->_process_value($_) for @$data }, HashRef, sub { $self->_process_value($_) for values %$data } +, Any, sub { croak "Could not grok data" }; }

The match_on_type function takes a value and a set of type–coderef pairs, dispatching to the first coderef where the value matches the type constraint. This function is stolen from Moose::Util::TypeConstraints.

You can get an order of magnitude faster though by doing something similar to what Type::Params does — compiling the match once, then calling it as needed.

Let's look at a naïve (and wrong) way to do this first and examine the problems:

use Types::Standard qw( ArrayRef HashRef Any ); use Type::Utils qw( compile_match_on_type ); use Carp qw( croak ); sub process_data { my ($self, $data) = @_; state $matcher = compile_match_on_type ArrayRef, sub { $self->_process_value($_) for @$data }, HashRef, sub { $self->_process_value($_) for values %$data } +, Any, sub { croak "Could not grok data" }; $matcher->($data); }

The big problem here is that the first time process_data is called, the matcher will close over $self and $data. Subsequent calls to $matcher will reuse the same closed over variables. Oops.

The simplest way of solving this is to take advantage of the fact that a compiled matcher (unlike match_on_type) can take a list of arguments, not just one. Only the first argument is used for the type matching, but all arguments are passed to the coderefs on dispatch.

use Types::Standard qw( ArrayRef HashRef Any ); use Type::Utils qw( compile_match_on_type ); use Carp qw( croak ); sub process_data { my ($self, $data) = @_; state $matcher = compile_match_on_type ArrayRef, sub { my ($d, $s) = @_; $s->_process_value($_) for +@$d }, HashRef, sub { my ($d, $s) = @_; $s->_process_value($_) for +values %$d }, Any, sub { croak "Could not grok data" }; $matcher->($data, $self); }

Like many Type::Tiny interfaces that expect coderefs, compile_match_on_type also accepts strings of Perl code as an alternative, and is able to optimize things better if those are supplied:

use Types::Standard qw( ArrayRef HashRef Any ); use Type::Utils qw( compile_match_on_type ); use Carp qw(); sub process_data { my ($self, $data) = @_; state $matcher = compile_match_on_type ArrayRef, q{ my ($d, $s) = @_; $s->_process_value($_) for @$d + }, HashRef, q{ my ($d, $s) = @_; $s->_process_value($_) for val +ues %$d }, Any, q{ Carp::croak("Could not grok data") }; $matcher->($data, $self); }

The coderefs compiled by compile_match_on_type should be very efficient. The technique is very similar to how Type::Coercion compiles coercions.


In reply to Exploring Type::Tiny Part 5: match_on_type by tobyink

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Are you posting in the right place? Check out Where do I post X? to know for sure.
  • Posts may use any of the Perl Monks Approved HTML tags. Currently these include the following:
    <code> <a> <b> <big> <blockquote> <br /> <dd> <dl> <dt> <em> <font> <h1> <h2> <h3> <h4> <h5> <h6> <hr /> <i> <li> <nbsp> <ol> <p> <small> <strike> <strong> <sub> <sup> <table> <td> <th> <tr> <tt> <u> <ul>
  • Snippets of code should be wrapped in <code> tags not <pre> tags. In fact, <pre> tags should generally be avoided. If they must be used, extreme care should be taken to ensure that their contents do not have long lines (<70 chars), in order to prevent horizontal scrolling (and possible janitor intervention).
  • Want more info? How to link or How to display code and escape characters are good places to start.