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:

  • undef

  • Plain 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:

    - Zero remaining arguments: []
    - One remaining argument: [ $value ]
    - Multiple remaining arguments: [ @values ]
  • 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 Any or Ref accept 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 signature is supported

  • No coercions or automatic type conversions.

  • No advanced parameter kinds.

    No parameter unions, parameter packs, or complex tuple types.

    The Type::Standard objects Slurpy['a] and Optional['a] are not recognized and therefore do not replace the parameters optional => 1 or slurpy => 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:

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.