Type::Params - Params::Validate-like parameter validation using Type::Tiny type constraints and coercions
use v5.12; use strict; use warnings; package Horse { use Moo; use Types::Standard qw( Object ); use Type::Params qw( compile ); use namespace::autoclean; ...; # define attributes, etc sub add_child { state $check = compile( Object, Object ); # method signature my ($self, $child) = $check->(@_); # unpack @_ push @{ $self->children }, $child; return $self; } } package main; my $boldruler = Horse->new; $boldruler->add_child( Horse->new ); $boldruler->add_child( 123 ); # dies (123 is not an Object!)
This module is covered by the Type-Tiny stability policy.
This documents the details of the Type::Params package. Type::Tiny::Manual is a better starting place if you're new.
Type::Params uses Type::Tiny constraints to validate the parameters to a sub. It takes the slightly unorthodox approach of separating validation into two stages:
Compiling the parameter specification into a coderef; then
Using the coderef to validate parameters.
The first stage is slow (it might take a couple of milliseconds), but you only need to do it the first time the sub is called. The second stage is fast; according to my benchmarks faster even than the XS version of Params::Validate.
If you're using a modern version of Perl, you can use the state keyword which was a feature added to Perl in 5.10. If you're stuck on Perl 5.8, the example from the SYNOPSIS could be rewritten as:
state
my $add_child_check; sub add_child { $add_child_check ||= compile( Object, Object ); my ($self, $child) = $add_child_check->(@_); # unpack @_ push @{ $self->children }, $child; return $self; }
Not quite as neat, but not awful either.
If you don't like the two step, there's a shortcut reducing it to one step:
use Type::Params qw( validate ); sub add_child { my ($self, $child) = validate(\@_, Object, Object); push @{ $self->children }, $child; return $self; }
Type::Params has a few tricks up its sleeve to make sure performance doesn't suffer too much with the shortcut, but it's never going to be as fast as the two stage compile/execute.
compile(@spec)
Given specifications for positional parameters, compiles a coderef that can check against them.
The generalized form of specifications for positional parameters is:
state $check = compile( \%general_opts, $type_for_arg_1, \%opts_for_arg_1, $type_for_arg_2, \%opts_for_arg_2, $type_for_arg_3, \%opts_for_arg_3, ..., Slurpy[...], );
If a hashref of options is empty, it can simply be omitted. Much of the time, you won't need to specify any options.
# In this example, we omit all the hashrefs # my $check = compile( Str, Int, Optional[ArrayRef], ); my ($str, $int, $arr) = $check->("Hello", 42, []); # ok my ($str, $int, $arr) = $check->("", -1); # ok my ($str, $int, $arr) = $check->("", -1, "bleh"); # dies
The coderef returned (i.e. $check) will check the arguments passed to it conform to the spec (coercing them if appropriate), and return them as a list if they do. If they don't, it will throw an exception.
$check
The first hashref, before any type constraints, is for general options which affect the entire compiled coderef. Currently supported general options are:
head
Parameters to shift off @_ before doing the main type check. These parameters may also be checked, and cannot be optional or slurpy. They may not have defaults.
@_
my $check = compile( { head => [ Int, Int ] }, Str, Str, ); # ... is basically the same as... my $check = compile( Int, Int, Str, Str, );
A number may be given if you do not care to check types:
my $check = compile( { head => 2 }, Str, Str, ); # ... is basically the same as... my $check = compile( Any, Any, Str, Str, );
This is mostly useless for compile, but can be useful for compile_named and compile_named_oo.
compile
compile_named
compile_named_oo
tail
Similar to head, but pops parameters off the end of @_ instead. This is actually useful for compile because it allows you to sneak in some required parameters after a slurpy or optional parameter.
my $check = compile( { tail => [ CodeRef ] }, Slurpy[ ArrayRef[Str] ], ); my ($strings, $coderef) = $check->("foo", "bar", sub { ... });
want_source
Instead of returning a coderef, return Perl source code string. Handy for debugging.
want_details
Instead of returning a coderef, return a hashref of stuff including the coderef. This is mostly for people extending Type::Params and I won't go into too many details about what else this hashref contains.
description
Description of the coderef that will show up in stack traces. Defaults to "parameter validation for X" where X is the caller sub name.
package
The package of the sub we're supposed to be checking. Not usually important. The default is probably fine.
subname
The name of the sub we're supposed to be checking. Not usually important. The default is probably fine.
If you wish to use the default description, but need to change the sub name, use this.
caller_level
If you wish to use the default description, but need to change the caller level for detecting the sub name, use this.
on_die
my $check = compile( { on_die => sub { ... } }, ..., ); my @args = $check->( @_ );
Normally, at the first invalid argument the $check coderef encounters, it will throw an exception.
If an on_die coderef is provided, then it is called instead, and the exception is passed to it as an object. The $check coderef will still immediately return though.
goto_next
Defaults to false.
This can be used to turn signatures inside out. Instead of your signature being a coderef which is called by your function, your function can be a coderef which is called by the signature.
# The function people call. sub foo { state $sig = compile( { goto_next => 1 }, Int ); $sig->( \&_real_foo, @_ ); } # Your real function which receives checked/coerced arguments sub _real_foo { my ( $n ) = ( @_ ); ...; }
Alternatively, using a coderef:
sub foo { state $sig = compile( { goto_next => \&_real_foo }, Int ); $sig->( @_ ); } sub _real_foo { my ( $n ) = ( @_ ); ...; }
Or even:
{ my $real_foo = sub { my ( $n ) = ( @_ ); ...; }; *foo = compile( { subname => 'foo', goto_next => $real_foo }, Int ); }
If you're using Moose/Mouse/Moo, then this should work:
sub foo { my ( $n ) = ( @_ ); ...; } around foo => compile( { subname => 'foo', goto_next => 1 }, Int );
strictness
If you set strictness to a false value (0, undef, or the empty string), then certain signature checks will simply never be done. The initial check that there's the correct number of parameters, plus type checks on parameters which don't coerce can be skipped.
If you set it to a true boolean (i.e. 1) or do not set it at all, then these checks will always be done.
Alternatively, it may be set to the quoted fully-qualified name of a Perl global variable or a constant, and that will be compiled into the coderef as a condition to enable strict checks.
state $check = compile( { strictness => '$::CHECK_TYPES' }, Int, ArrayRef, ); # Type checks are skipped { local $::CHECK_TYPES = 0; my ( $number, $list ) = $check->( {}, {} ); } # Type checks are performed { local $::CHECK_TYPES = 1; my ( $number, $list ) = $check->( {}, {} ); }
A recommended use of this is with Devel::StrictMode.
use Devel::StrictMode qw( STRICT ); state $check = compile( { strictness => STRICT }, Int, ArrayRef );
The types for each parameter may be any Type::Tiny type constraint, or anything that Type::Tiny knows how to coerce into a Type::Tiny type constraint, such as a MooseX::Types type constraint or a coderef.
Type coercions are automatically applied for all types that have coercions.
If you wish to avoid coercions for a type, use Type::Tiny's no_coercions method.
no_coercions
my $check = compile( Int, ArrayRef->of(Bool)->no_coercions, );
Note that having any coercions in a specification, even if they're not used in a particular check, will slightly slow down $check because it means that $check can't just check @_ and return it unaltered if it's valid — it needs to build a new array to return.
Optional parameters can be given using the Optional[] type constraint. In the example above, the third parameter is optional. If it's present, it's required to be an arrayref, but if it's absent, it is ignored.
Optional parameters need to be after required parameters in the spec.
An alternative way to specify optional parameters is using a parameter options hashref.
my $check = compile( Str, Int, ArrayRef, { optional => 1 }, );
The following parameter options are supported:
optional
This is an alternative way of indicating that a parameter is optional.
state $check = compile( Int, Int, { optional => 1 }, Optional[Int], );
The two are not exactly equivalent. The exceptions thrown will differ in the type name they mention. (Int versus Optional[Int].)
default
A default may be provided for a parameter.
state $check = compile( Int, Int, { default => "666" }, Int, { default => "999" }, );
Supported defaults are any strings (including numerical ones), undef, and empty hashrefs and arrayrefs. Non-empty hashrefs and arrayrefs are not allowed as defaults.
undef
Alternatively, you may provide a coderef to generate a default value:
state $check = compile( Int, Int, { default => sub { 6 * 111 } }, Int, { default => sub { 9 * 111 } }, );
That coderef may generate any value, including non-empty arrayrefs and non-empty hashrefs. For undef, simple strings, numbers, and empty structures, avoiding using a coderef will make your parameter processing faster.
Instead of a coderef, you can use a reference to a string of Perl source code:
state $check = compile( Int, Int, { default => \ '6 * 111' }, Int, { default => \ '9 * 111' }, );
The default will be validated against the type constraint, and potentially coerced.
Note that having any defaults in a specification, even if they're not used in a particular check, will slightly slow down $check because it means that $check can't just check @_ and return it unaltered if it's valid — it needs to build a new array to return.
clone
If this is set to true, it will deep clone incoming values via dclone from Storable (a core module since Perl 5.7.3).
dclone
In the below example, $arr is a reference to a clone of @numbers, so pushing additional numbers to it leaves @numbers unaffected.
$arr
@numbers
sub foo { state $check = compile( ArrayRef, { clone => 1 } ); my ( $arr ) = &$check; push @$arr, 4, 5, 6; } my @numbers = ( 1, 2, 3 ); foo( \@numbers ); print "@numbers\n"; ## 1 2 3
Overrides the signature-wide strictness setting on a per-parameter basis.
slurpy
The following two should be equivalent:
my $check = compile( Int, Slurpy[ArrayRef] ); my $check = compile( Int, ArrayRef, { slurpy => 1 } );
As a special case, the numbers 0 and 1 may be used as shortcuts for Optional[Any] and Any.
# Positional parameters state $check = compile(1, 0, 0); my ($foo, $bar, $baz) = $check->(@_); # $bar and $baz are optional
After any required and optional parameters may be a slurpy parameter. Any additional arguments passed to $check will be slurped into an arrayref or hashref and checked against the slurpy parameter. Defaults are not supported for slurpy parameters.
Example with a slurpy ArrayRef:
sub xyz { state $check = compile( Int, Int, Slurpy[ ArrayRef[Int] ] ); my ($foo, $bar, $baz) = $check->(@_); } xyz(1..5); # $foo = 1 # $bar = 2 # $baz = [ 3, 4, 5 ]
Example with a slurpy HashRef:
my $check = compile( Int, Optional[Str], Slurpy[ HashRef[Int] ], ); my ($x, $y, $z) = $check->(1, "y", foo => 666, bar => 999); # $x is 1 # $y is "y" # $z is { foo => 666, bar => 999 }
Any type constraints derived from ArrayRef or HashRef should work, but a type does need to inherit from one of those because otherwise Type::Params cannot know what kind of structure to slurp the remaining arguments into.
Slurpy[Any] is also allowed as a special case, and is treated as Slurpy[ArrayRef].
From Type::Params 1.005000 onwards, slurpy hashrefs can be passed in as a true hashref (which will be shallow cloned) rather than key-value pairs.
sub xyz { state $check = compile(Int, Slurpy[HashRef]); my ($num, $hr) = $check->(@_); ... } xyz( 5, foo => 1, bar => 2 ); # works xyz( 5, { foo => 1, bar => 2 } ); # works from 1.005000
This feature is only implemented for slurpy hashrefs, not slurpy arrayrefs.
Note that having a slurpy parameter will slightly slow down $check because it means that $check can't just check @_ and return it unaltered if it's valid — it needs to build a new array to return.
validate(\@_, @spec)
This example of compile:
sub foo { state $check = compile(@spec); my @args = $check->(@_); ...; }
Can be written using validate as:
validate
sub foo { my @args = validate(\@_, @spec); ...; }
Performance using compile will always beat validate though.
compile_named(@spec)
compile_named is a variant of compile for named parameters instead of positional parameters.
The format of the specification is changed to include names for each parameter:
state $check = compile_named( \%general_opts, foo => $type_for_foo, \%opts_for_foo, bar => $type_for_bar, \%opts_for_bar, baz => $type_for_baz, \%opts_for_baz, ..., extra => Slurpy[...], );
The $check coderef will return a hashref.
my $check = compile_named( foo => Int, bar => Str, { default => "hello" }, ); my $args = $check->(foo => 42); # $args->{foo} is 42 # $args->{bar} is "hello"
The %general_opts hash supports the same options as compile plus a few additional options:
%general_opts
class
The check coderef will, instead of returning a simple hashref, call $class->new($hashref) and return the result.
$class->new($hashref)
constructor
Specifies an alternative method name instead of new for the class option described above.
new
Shortcut for declaring both the class and constructor options at once.
bless
Like class, but bypass the constructor and directly bless the hashref.
named_to_list
Instead of returning a hashref, return a hash slice.
myfunc(bar => "x", foo => "y"); sub myfunc { state $check = compile_named( { named_to_list => 1 }, foo => Str, { optional => 1 }, bar => Str, { optional => 1 }, ); my ($foo, $bar) = $check->(@_); ...; ## $foo is "y" and $bar is "x" }
The order of keys for the hash slice is the same as the order of the names passed to compile_named. For missing named parameters, undef is returned in the list.
Basically in the above example, myfunc takes named parameters, but receieves positional parameters.
myfunc
As above, but explicitly specify the keys of the hash slice.
Like compile, the numbers 0 and 1 may be used as shortcuts for Optional[Any] and Any.
state $check = compile_named(foo => 1, bar => 0, baz => 0); my $args = $check->(@_); # $args->{bar} and $args->{baz} are optional
Slurpy parameters are slurped into a nested hashref.
my $check = compile( foo => Str, bar => Optional[Str], extra => Slurpy[ HashRef[Str] ], ); my $args = $check->(foo => "aaa", quux => "bbb"); print $args->{foo}, "\n"; # aaa print $args->{extra}{quux}, "\n"; # bbb
slurpy[Any] is treated as slurpy[HashRef].
The head and tail options are supported. This allows for a mixture of positional and named arguments, as long as the positional arguments are non-optional and at the head and tail of @_.
my $check = compile( { head => [ Int, Int ], tail => [ CodeRef ] }, foo => Str, bar => Str, baz => Str, ); my ($int1, $int2, $args, $coderef) = $check->( 666, 999, foo=>'x', bar=>'y', baz=>'z', sub {...} ); say $args->{bar}; # 'y'
This can be combined with named_to_list:
my $check = compile( { head => [ Int, Int ], tail => [ CodeRef ], named_to_list => 1 }, foo => Str, bar => Str, baz => Str, ); my ($int1, $int2, $foo, $bar, $baz, $coderef) = $check->( 666, 999, foo=>'x', bar=>'y', baz=>'z', sub {...} ); say $bar; # 'y'
There is one additional parameter option supported, in addition to the optional, default, clone, and slurpy options already supported by positional parameters.
alias
A list of alternative names for the parameter, or a single alternative name.
{ my $check; sub adder { $check ||= compile_named( first_number => Int, { alias => [ 'x' ] }, second_number => Int, { alias => 'y' }, ); my ( $arg ) = &$check; return $arg->{first_number} + $arg->{second_number}; } } say adder( first_number => 40, second_number => 2 ); # 42 say adder( x => 40, y => 2 ); # 42 say adder( first_number => 40, y => 2 ); # 42 say adder( first_number => 40, x => 1, y => 2 ); # dies!
validate_named(\@_, @spec)
Like compile has validate, compile_named has validate_named. Just like validate, it's the slower way to do things, so stick with compile_named.
validate_named
compile_named_oo(@spec)
Here's a quick example function:
sub add_contact_to_database { state $check = compile_named( dbh => Object, id => Int, name => Str, ); my $arg = $check->(@_); my $sth = $arg->{db}->prepare('INSERT INTO contacts VALUES (?, ?)'); $sth->execute($arg->{id}, $arg->{name}); }
Looks simple, right? Did you spot that it will always die with an error message Can't call method "prepare" on an undefined value?
This is because we defined a parameter called 'dbh' but later tried to refer to it as $arg{db}. Here, Perl gives us a pretty clear error, but sometimes the failures will be far more subtle. Wouldn't it be nice if instead we could do this?
$arg{db}
sub add_contact_to_database { state $check = compile_named_oo( dbh => Object, id => Int, name => Str, ); my $arg = $check->(@_); my $sth = $arg->dbh->prepare('INSERT INTO contacts VALUES (?, ?)'); $sth->execute($arg->id, $arg->name); }
If we tried to call $arg->db, it would fail because there was no such method.
$arg->db
Well, that's exactly what compile_named_oo does.
As well as giving you nice protection against mistyped parameter names, It also looks kinda pretty, I think. Hash lookups are a little faster than method calls, of course (though Type::Params creates the methods using Class::XSAccessor if it's installed, so they're still pretty fast).
An optional parameter foo will also get a nifty $arg->has_foo predicate method. Yay!
foo
$arg->has_foo
compile_named_oo gives you some extra options for parameters, in addition to the optional, default, clone, slurpy, and alias options already supported by compile_named.
sub add_contact_to_database { state $check = compile_named_oo( dbh => Object, id => Int, { default => '0', getter => 'identifier' }, name => Str, { optional => 1, predicate => 'has_name' }, ); my $arg = $check->(@_); my $sth = $arg->dbh->prepare('INSERT INTO contacts VALUES (?, ?)'); $sth->execute($arg->identifier, $arg->name) if $arg->has_name; }
getter
The getter option lets you choose the method name for getting the argument value.
If the parameter has an alias, this currently does not result in additional getters being defined.
predicate
The predicate option lets you choose the method name for checking the existence of an argument. By setting an explicit predicate method name, you can force a predicate method to be generated for non-optional arguments.
If the parameter has an alias, this currently does not result in additional predicate methods being defined.
The objects returned by compile_named_oo are blessed into lightweight classes which have been generated on the fly. Don't expect the names of the classes to be stable or predictable. It's probably a bad idea to be checking can, isa, or DOES on any of these objects. If you're doing that, you've missed the point of them.
can
isa
DOES
They don't have any constructor (new method). The $check coderef effectively is the constructor.
validate_named_oo(\@_, @spec)
This function doesn't even exist. :D
multisig(@alternatives)
Type::Params can export a multisig function that compiles multiple alternative signatures into one, and uses the first one that works:
multisig
state $check = multisig( [ Int, ArrayRef ], [ HashRef, Num ], [ CodeRef ], ); my ($int, $arrayref) = $check->( 1, [] ); # okay my ($hashref, $num) = $check->( {}, 1.1 ); # okay my ($code) = $check->( sub { 1 } ); # okay $check->( sub { 1 }, 1.1 ); # throws an exception
Coercions, slurpy parameters, etc still work.
The magic global ${^TYPE_PARAMS_MULTISIG} is set to the index of the first signature which succeeded.
${^TYPE_PARAMS_MULTISIG}
The present implementation involves compiling each signature independently, and trying them each (in their given order!) in an eval block. The only slightly intelligent part is that it checks if scalar(@_) fits into the signature properly (taking into account optional and slurpy parameters), and skips evals which couldn't possibly succeed.
eval
scalar(@_)
It's also possible to list coderefs as alternatives in multisig:
state $check = multisig( [ Int, ArrayRef ], sub { ... }, [ HashRef, Num ], [ CodeRef ], compile_named( needle => Value, haystack => Ref ), );
The coderef is expected to die if that alternative should be abandoned (and the next alternative tried), or return the list of accepted parameters. Here's a full example:
sub get_from { state $check = multisig( [ Int, ArrayRef ], [ Str, HashRef ], sub { my ($meth, $obj) = @_; die unless is_Object($obj); die unless $obj->can($meth); return ($meth, $obj); }, ); my ($needle, $haystack) = $check->(@_); for (${^TYPE_PARAMS_MULTISIG}) { return $haystack->[$needle] if $_ == 0; return $haystack->{$needle} if $_ == 1; return $haystack->$needle if $_ == 2; } } get_from(0, \@array); # returns $array[0] get_from('foo', \%hash); # returns $hash{foo} get_from('foo', $obj); # returns $obj->foo
The default error message is just "Parameter validation failed". You can pass an option hashref as the first argument with an informative message string:
"Parameter validation failed"
sub foo { state $OptionsDict = Dict[...]; state $check = multisig( { message => 'USAGE: $object->foo(\%options?, $string)' }, [ Object, $OptionsDict, StringLike ], [ Object, StringLike ], ); my ($self, @args) = $check->(@_); my ($opts, $str) = ${^TYPE_PARAMS_MULTISIG} ? ({}, @args) : @_; ...; } $obj->foo(\%opts, "Hello"); $obj->foo("World");
wrap_subs( $subname1, $wrapper1, ... )
It's possible to turn the check inside-out and instead of the sub calling the check, the check can call the original sub.
Normal way:
use Type::Param qw(compile); use Types::Standard qw(Int Str); sub foobar { state $check = compile(Int, Str); my ($foo, $bar) = @_; ...; }
Inside-out way:
use Type::Param qw(wrap_subs); use Types::Standard qw(Int Str); sub foobar { my ($foo, $bar) = @_; ...; } wrap_subs foobar => [Int, Str];
wrap_subs takes a hash of subs to wrap. The keys are the sub names and the values are either arrayrefs of arguments to pass to compile to make a check, or coderefs that have already been built by compile, compile_named, or compile_named_oo.
wrap_subs
wrap_methods( $subname1, $wrapper1, ... )
wrap_methods also exists, which shifts off the invocant from @_ before the check, but unshifts it before calling the original sub.
wrap_methods
use Type::Param qw(wrap_methods); use Types::Standard qw(Int Str); sub foobar { my ($self, $foo, $bar) = @_; ...; } wrap_methods foobar => [Int, Str];
Type::Params exports a type Invocant on request. This gives you a type constraint which accepts classnames and blessed objects.
use Type::Params qw( compile Invocant ); sub my_method { state $check = compile(Invocant, ArrayRef, Int); my ($self_or_class, $arr, $ix) = $check->(@_); return $arr->[ $ix ]; }
Type::Params exports a parameterizable type constraint ArgsObject. It accepts the kinds of objects returned by compile_named_oo checks.
package Foo { use Moo; use Type::Params 'ArgsObject'; has args => ( is => 'ro', isa => ArgsObject['Bar::bar'], ); } package Bar { use Types::Standard -types; use Type::Params 'compile_named_oo'; sub bar { state $check = compile_named_oo( xxx => Int, yyy => ArrayRef, ); my $args = &$check; return 'Foo'->new( args => $args ); } } Bar::bar( xxx => 42, yyy => [] );
The parameter "Bar::bar" refers to the caller when the check is compiled, rather than when the parameters are checked.
PERL_TYPE_PARAMS_XS
Affects the building of accessors for compile_named_oo. If set to true, will use Class::XSAccessor. If set to false, will use pure Perl. If this environment variable does not exist, will use Class::XSAccessor if it is available.
Please report any bugs to https://github.com/tobyink/p5-type-tiny/issues.
The Type::Tiny homepage.
Type::Tiny, Type::Coercion, Types::Standard.
Toby Inkster <tobyink@cpan.org>.
This software is copyright (c) 2013-2014, 2017-2022 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
To install Type::Tiny, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Type::Tiny
CPAN shell
perl -MCPAN -e shell install Type::Tiny
For more information on module installation, please visit the detailed CPAN module installation guide.