The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Arguments - Perl subroutine type-checking

SYNOPSIS

(This documents version 0.2 of Arguments.)

  package Flintstone;

  use Arguments;

  BEGIN {
    $Arguments::ARGUMENT_CHECKS{INTEGER}
      = sub { defined $_[0] and $_[0] =~ /^[+-]?\d+$/ },
  }

  sub fooby ($\%) : Arguments (INTEGER, HASH);
  sub tv_show ($) : Arguments (Flintstone);

DESCRIPTION

Arguments provides argument checking during compile and run time, supplementing prototype declarations.

Why?

There are other ways of doing this -- Damian Conway's Attribute::Handlers and Attribute::Types are one very interesting route; Dave Rolsky's Params::Validate is another. I am doubtful if I have covered the gamut with the mention of just those two.

However, I had an epiphany to use subroutine attributes for argument type checking, and to try and make it clean and simple to use (DCONWAY's work is too general-purpose for my needs, and has a lot of overhead). It is not there yet, but I hope to get it there. If nothing else, it is a new, fun area of Perl for me to explore.

An obvious area to explore is reimplementing this module using Attribute::Handlers and hooking in Params::Validate for richer type-checking. Maybe I'll do that after installing L4. :-)

Basic Use

To use Arguments, a sub declares an attribute named Arguments listing the type of arguments, each matching a protype declaration:

  sub fooby ($\%) : Arguments (INTEGER, HASH);

By default, Arguments has only two checks:

Regular Expressions

Any argument to the Arguments attribute starting with a / (the forward-slash character) is assumed to be the beginning of a regular expression formed by appending that argument to qr. See "qr/STRING/imosx" in perlop for details. Arguments to the sub call are then checked against this pattern. An example:

  sub eat_int_and_live ($) : Arguments (/^[+-]?\d+$/);
References (the default)

Any other argument is assumed to be a reference checked by UNIVERSAL::isa. This includes non-blessed reference types such as HASH. An example:

  sub eat_code_and_die (&) : Arguments (CODE);

This example is unexciting since Perl's own prototype-checking should catch argument mismatches.

Enforcing Method Calls

A more interesting example enforces method calls:

  package Flintstone;

  sub yabba_dabba_doo ($) : Arguments (Flintstone);

The creates a run-time check that the first argument to yabba_dabba_doo is indeed a Flintstone or a package which has Flintstone as a base. Presently, Perl has no way of enforcing this restriction.

Extending Argument Checks

Packages may extend the argument checks by manipulating %Arguments::ARGUMENT_CHECKS in their BEGIN blocks. An example:

  BEGIN {
    $Arguments::ARGUMENT_CHECKS{INTEGER}
      = sub { defined $_[0] and $_[0] =~ /^[+-]?\d+$/ },
  }

  sub eat_int_and_live ($) : Arguments (INTEGER);

This is the same as the example above for regular expressions, except that the intent of the sub declaration is more clear.

EXPORT

None. However, Arguements pushes itself onto the caller's @ISA array so that the MODIFY_CODE_ATTRIBUTES technique may work. See "Package-specific Attribute Handling" in attributes for an explanation.

DIAGNOSTICS

The following are the diagnostics generated by Arguments. Items marked "(W)" are non-fatal (invoke Carp::carp); those marked "(F)" are fatal (invoke Carp::croak). None of the diagnostics may be selectively disabled with categores. See <perllexwarn>.

Can't use string ("%s") as %s ref while "strict refs" in use

(F) Only hard references are allowed by strict refs. Symbolic references are disallowed. See perlref.

What this usually means for Arguments is that you have a poorly formed argument list to the Arguments attribute such as Arguments (Apple Core) instead of Arguments (Apple, Core).

(F) The function requires more arguments than you specified.

Not enough prototypes for %s

(F) The function requires more prototypes in the Arguments attribute than you specified.

Too many arguments for %s

(F) The function requires fewer arguments than you specified.

Too many prototypes for %s

(F) The function requires fewer prototypes in the Arguments attribute than you specified.

Type of arg %d to %s must be %s (not %s)

(F) This function requires the argument in that position to be of a certain type. Arrays must be @NAME or @{EXPR}. Hashes must be %NAME or %{EXPR}. No implicit dereferencing is allowed--use the {EXPR} forms as an explicit dereference. See perlref. For blessed references, UNIVERSAL::isa ($_[%d], '%s') need be true.

TODO

Tie type-checking of prototypes and attributes.
Support for non-scalar prototypes (e.g., sub (\@)).
Support for optional prototypes (e.g., sub ($;$)).
Support for list prototypes (e.g., sub (%)).
Generate prototype declarations from the attributes.
Tests.

AUTHOR

B. K. Oxley (binkley) <binkley@bigfoot.com<

SEE ALSO

Attribute::Handlers
Attribute::Types
"isa ( TYPE )" in UNIVERSAL
attributes
"qr/STRING/imosx" in perlop
perllexwarn
perlref

1 POD Error

The following errors were encountered while parsing the POD:

Around line 427:

=over without closing =back