Not satisfied with any of the Class:: modules as the basis for the 50+ module distro I'm working on, I rolled my own. It's too specific to be published independently, but here's the rundown...

The following routine, which simply verifies that label names exist in a defaults hash, is always available, though it is called almost exclusively by a single constructor which nearly all the classes inherit.

sub verify_args { my $defaults = shift; # leave the rest of @_ intact. # Verify that args came in pairs. croak "Expecting key => value pairs, but got odd number of args" if @_ % 2; # Verify keys, ignore values. while (@_) { my ($var, undef) = (shift, shift); croak "Invalid parameter: '$var'" unless exists $defaults->{$var}; } }

So... my example would be:

my %convert_defaults = ( from => 'here' to => 'eternity' thing => undef, ); sub convert { verify_args(\%convert_defaults, @_); my %args = (%convert_defaults, @_); ... }

For the small minority of performance-critical subs, such as constructors for frequently instantiated classes, I try to design things so that it's reasonably safe to skip verification -- for example requiring by very few arguments, or by using clone() against a template.

All subclasses of the base class KinoSearch::Util::Class inherit new() and init_instance() (among others).

Here's the important stuff from the base class:

package KinoSearch::Util::Class; use KinoSearch::Util::ToolSet; # strict, warnings, Carp, etc. use Clone 'clone'; use KinoSearch::Util::VerifyArgs qw( verify_args ); our %instance_vars = (); sub new { my $class = shift; # leave the rest of @_ intact. # Clone the instance_vars hash and bless it. $class = ref($class) || $class; my $defaults; { no strict 'refs'; $defaults = \%{ $class . '::instance_vars' }; } my $self = clone($defaults); bless $self, $class; # Verify argument labels. eval { verify_args( $defaults, @_ ) }; if ($@) { my ( $package, $filename, $line ) = caller; die "ERROR during attempt to create object of class '$class' \ +n" . " at $filename line $line:\n" . " $@\n"; } # Merge var => val pairs into object. %$self = ( %$self, @_ ); $self->init_instance; return $self; } sub init_instance { } # Class method to build the %instance_vars hash sub init_instance_vars { my $package = shift; no strict 'refs'; my $first_isa = ${ $package . '::ISA' }[0]; return ( %{ $first_isa . '::instance_vars' }, @_ ); } # use to define an abstract method sub abstract_death { my ( undef, $filename, $line, $methodname ) = caller(1); die "ERROR: $methodname', called at $filename line $line, is an " . "abstract method and must be defined in a subclass."; }

Here's the Analyzer abstract class which defines a single instance variable, language, and a single sub, analyze.

package KinoSearch::Analysis::Analyzer; use KinoSearch::Util::ToolSet; use base qw( KinoSearch::Util::Class ); our %instance_vars = __PACKAGE__->init_instance_vars( language => '', +); sub analyze { shift->abstract_death } 1;

... and here's the PolyAnalyzer class which inherits from it. new(), which is inherited from KinoSearch::Util::Class, verifies the label names. init_instance() does a little bit more, checking to make sure that either language or analyzers is defined.

package KinoSearch::Analysis::PolyAnalyzer; use KinoSearch::Util::ToolSet; use base qw( KinoSearch::Analysis::Analyzer ); use KinoSearch::Analysis::LCNormalizer; use KinoSearch::Analysis::Tokenizer; use KinoSearch::Analysis::Stemmer; our %instance_vars = __PACKAGE__->init_instance_vars( analyzers => und +ef, ); sub init_instance { my $self = shift; my $language = $self->{language} = lc( $self->{language} ); if ( !defined $self->{analyzers} ) { croak("Must specify either 'language' or 'analyzers'") unless $language; $self->{analyzers} = [ KinoSearch::Analysis::LCNormalizer->new( language => $lang +uage ), KinoSearch::Analysis::Tokenizer->new( language => $lang +uage ), KinoSearch::Analysis::Stemmer->new( language => $lang +uage ), ]; } } sub analyze { my ( $self, $field ) = @_; $_->analyze($field) for @{ $self->{analyzers} }; } 1;

The parameter verification in PolyAnalyzer's init_instance isn't that strong -- language isn't tested to see if it's a valid value, and analyzers isn't checked to verify that it's an arrayref -- but that's because I know that meaningful exceptions will be thrown somewhere down the line if invalid values are supplied.

The OO design is heavily influenced by Java, as the project is a loose port of Java Lucene and this scheme allows for a lot of parallel coding. However, the signature-based parameter verification in Lucene often leaves me scratching my head, e.g. as to what the "true" in fooThis(query, file, true) does. With signatures unavailable, I'm forced to use named parameters in Perl -- but in some ways I like 'em better anyway.

--
Marvin Humphrey
Rectangular Research ― http://www.rectangular.com

In reply to Re: Your named arguments by creamygoodness
in thread Your named arguments by Juerd

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



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.