NAME
TUI::toolkit::Params - Lightweight signature validation inspired by Type::Params
SYNOPSIS
use TUI::toolkit::Params qw( signature );
use Type::Standard qw( ArrayRef Int );
use Scalar::Util qw( looks_like_number );
# Basic example
sub add_numbers {
state $sig = signature(
pos => [
Int, # Type::API / Type::Tiny compatible object
sub { /^\d+$/ }, # custom CODE predicate
],
);
my ($x, $y) = $sig->(@_);
return $x + $y;
}
say add_numbers(3, 5); # ok
say add_numbers(3, "abc"); # dies with descriptive error message
# Example with slurpy parameter
sub sum {
state $sig = signature(
pos => [
Int,
ArrayRef[Int], { slurpy => 1 },
],
);
my ($first, $rest) = $sig->(@_); # $rest is an arrayref
my $sum = $first;
$sum += $_ for @$rest;
return $sum;
}
say sum(1, 2, 3, 4); # ok
say sum(1, "x"); # dies with descriptive error
# Example with optional parameters
sub vector_length {
state $sig = signature(
pos => [
\&looks_like_number, # x (mandatory)
\&looks_like_number, { optional => 1 }, # y (optional)
\&looks_like_number, { optional => 1 }, # z (optional)
],
);
my ($x, $y, $z) = $sig->(@_);
# Missing optional components default to zero
$y //= 0;
$z //= 0;
return sqrt($x*$x + $y*$y + $z*$z);
}
say vector_length(3, 4); # 2D -> 5
say vector_length(3, 4, 12); # 3D -> 13
# Example with defaults
sub compute_magic {
state $sig = signature(
pos => [
Int, # required
Int, { default => 10 }, # default integer value
Int, { default => "999" }, # default string
Int, { default => sub { 2 * 21 } }, # compiled into 42
Int, { default => \'6 * 111' }, # smarter into 666
],
);
my ($x, $y, $a, $b, $c) = $sig->(@_);
return $x * $y + $a + $b + $c;
}
# Named example
sub create_user {
state $sig = signature(
named => [
name => Str,
age => Int, { optional => 1 },
],
);
my ($args) = $sig->(@_);
return "User $args->{name}, $args->{age}";
}
# Method signatures
sub do_it {
state $sig = signature(
method => Object,
pos => [ Int ],
);
my ($self, $i) = $sig->(@_);
...;
}
DESCRIPTION
The function signature provides a lightweight mechanism for validating positional arguments to Perl subroutines. It is inspired by Type::Params, but intentionally simpler and without dependencies on XS or Perl components that are not part of the standard distribution.
The module accepts a list of type specifications and returns a compiled checker subroutine. This checker performs:
arity validation
type checking via Type::API or plain CODE predicates
default value handling
optional parameters
method invocant support
slurpy parameters (hash or array semantics)
named alias resolution
All type checking logic is compiled once at signature creation time, resulting in faster validation than doing checks inside the subroutine body.
SIGNATURE SPECIFICATION
pos / positional
signature(
pos => [
Int,
Str, { optional => 1 },
HashRef, { slurpy => 1 },
],
);
Positional parameters are specified as alternating TYPE and optional \{OPTIONS\} entries.
named
signature(
named => [
foo => Int,
bar => Str, { optional => 1 },
baz => HashRef, { slurpy => 1 },
],
);
Named signatures take key/value pairs followed by optional option hashes.
method
The method option provides syntactic sugar for defining method invocants in a positional signature. It prepends an additional, non-optional positional parameter to the beginning of the signature.
This parameter is treated exactly like any other entry in pos, and supports the same type specifications (Type::Tiny, Type::API, CODE predicates, etc.).
method => 1
Using method => 1 prepends a predicate for a basic check. This means:
An invocant is required (arity increases by one).
The invocant is not undefined.
The implementation simply uses a
sub { defined }predicate.
Example:
signature(
method => 1,
pos => [ Int, Str ],
);
behaves exactly like:
signature(
pos => [ sub { defined }, Int, Str ],
);
method => $isa
If the argument is a type object or predicate, it is prepended as-is to the positional parameter list. This allows expressing object or class method signatures declaratively.
Examples:
signature(
method => Object,
pos => [ Int ],
);
is equivalent to:
signature(
pos => [ Object, Int ],
);
If neither pos nor positional is provided, an empty positional list is created automatically, and the method parameter is prepended. This allows signatures consisting solely of an invocant.
PARAMETER OPTIONS
optional
Optional parameters may be declared by attaching a hashref:
pos => [
Int,
Str, { optional => 1 },
Str, { optional => 1 },
]
Rules:
Optional parameters must appear as one continuous block at the end of the non-slurpy parameters on positional signatures.
A mandatory parameter after an optional parameter is an error for positional signatures.
Missing optional values are returned as
undef.
default
Parameters may define a default value using default => ... inside the option hashref:
positional => [
Int,
Int, { default => 42 }, # simple scalar
Int, { default => \"333 * 2" }, # string ref
];
Any parameter with a default is automatically optional.
Supported forms of default values are:
undefPlain non-reference scalars (strings or numbers)
Empty arrayrefs (
[])Empty hashrefs (
{})CODE references, which are executed to generate the default value
SCALAR references containing a string of Perl source code
Unsupported defaults will cause an exception at signature construction time.
Default values are validated against the parameter type, just like explicit arguments.
slurpy
A single slurpy parameter may be declared:
pos => [
Int,
ArrayRef[Int], { slurpy => 1 },
]
Rules:
The slurpy parameter must be the last entry.
Only one slurpy parameter is allowed.
A slurpy parameter must have a type constraint that accepts the value produced by slurpy processing. In array-slurpy mode this value is an array reference; in hash-slurpy mode it is a hash reference.
Slurpy processing produces either an array reference or a hash reference, depending on the slurpy mode. This value is then validated against the type constraint of the slurpy parameter.
Array-slurpy
Remaining arguments are collected into an array reference:
Hash-slurpy
Remaining arguments are collected into a hash reference:
- - Zero remaining arguments: {}
- - One remaining argument: ( ref $value eq 'HASH' ) ? $value : { $value }
- - Multiple remaining arguments: { @values }
Note: Simple types such as
AnyorRefaccept these structures and are therefore valid slurpy parameter types, resulting in an array reference.
alias (named only)
Declares one or more alias names for a named parameter.
LIMITATIONS
This module implements only a subset of Type::Params. Notable limits:
Only
signatureis supportedNo coercions or automatic type conversions.
No advanced parameter kinds.
No parameter unions, parameter packs, or complex tuple types.
The Type::Standard objects
Slurpy['a]andOptional['a]are not recognized and therefore do not replace the parametersoptional => 1orslurpy => 1.Having any optionals, default or aliases disables the fast-path.
Note that having any parameter with a optional or default disables the fast-path optimization in which the validator can return
@_unchanged.
REQUIRES
Only core modules are used:
Perl 5.10+
SEE ALSO
AUTHOR
J. Schneider <brickpool@cpan.org>
CONTRIBUTORS
Toby Inkster <tobyink@cpan.org>
LICENSE
Copyright (c) 2013-2014, 2017-2026 the "AUTHORS" and "CONTRIBUTORS" as listed above.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.