John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

I want to write some methods that take named parameters, for all the usual reasons. The basics is very simple -- just pop the @_ array into a hash. But, deatils and nuances will expand that code. For example, noting unknown parameters rather than ignoring them; and handling default values. When doing a bunch of functions, the code becomes repetitive and it makes sence to have a common module to do it.

So, is there a nice robust and feature-rich module already published that does this? Or, got an example of how you do it?

I'm specifically interested in common validation code. Just off the top of my head, I'm thinking that a prototype hash can list the keys allowed along with values that mean something to the validation process. Or, if only testing for unknown parameters, a single function that checks to see if one list is not a subset of another list would do the trick. But, why not give details and help info as well, much in the same way as command-line arguments?

—John

Replies are listed 'Best First'.
Re: Passing by Named Parameter
by dragonchild (Archbishop) on Jan 26, 2004 at 22:21 UTC
    Params::Validate has most of what you're looking for. I looked into it and it could do with some strengthening, but it's an excellent start. (I didn't end up using it because I refactored to less-comprehensive function signatures, but that was a design question - I didn't really need it.)

    Updated as per ysth's reply

    ------
    We are the carpenters and bricklayers of the Information Age.

    Please remember that I'm crufty and crochety. All opinions are purely mine and all code is untested, unless otherwise specified.

      I looked into it and it could do with some strengthening, but it's an excellent start.

      Specific suggestions and/or patches should be sent to the module author (*cough*).

        I think I'll take a close look at it.

        As for suggestions, I see that it has dependant parameters but not mutually-exclusive parameters.

        I wanted to get some replies before I put any effort into re-inventing anything, but my musings indicate that some possible features can be order-dependant, which is a problem with the use of a hash for a prototype. I'm also worried about slowing it down if it's too fancy!

        —John

Re: Passing by Named Parameter
by kvale (Monsignor) on Jan 26, 2004 at 22:14 UTC
    I think you answered you own question: look at the code in Getopt::Long.

    -Mark

Re: Passing by Named Parameter
by tilly (Archbishop) on Jan 27, 2004 at 02:06 UTC
      That's what I did the other day when I only needed one function. That is, I removed each arg when using it and then if there's anything left it's an error. I wished for the Perl6 // operator, but by checking for an undef value rather than definedness in the hash, I only mentioned the actual key once and so didn't bother making it a helper function.

      I think I'll use your function (or one like it) next time I want simple argument naming. Thanks.

Re: Passing by Named Parameter
by Coruscate (Sexton) on Jan 27, 2004 at 05:37 UTC

    You could always use something like what I just threw together below. But like you said, the code becomes repetitive. Here it is: provides both parameter checking and default values.

    sub do_something { my %default = ( # declare all valid parameter foo => 'foo argument', # names, providing a default bar => 'bar argument', # value at the same time (set baz => undef # to undef for no default) ); my %params = (%default, @_); for (keys %params) { warn "unknown parameter '$_' passed to do_something()" unless exists $default{$_}; } # now do stuff. }

Re: Passing by Named Parameter
by davido (Cardinal) on Jan 27, 2004 at 06:00 UTC
    I might do something like this:

    sub mysub { %params = %{ +shift }; my %known_options = ( 'this' => 1, 'that' => 2 ); PARSE: foreach my $param ( keys %params ) { exists $known_options{$param} && do { #something; next PARSE; }; die "Usage: I only know how to 'this' and 'that'\n"; } # Rest of sub here... }

    Just One Way To Do It.

    The values assigned to keys in %known_options are just numeric for this example, but could be coderefs, subrefs, flag values, or whatever you want.

    Update: Better late than never, fixed %params = %{ +shift }; syntax.


    Dave

      %params = %{ shift };
      When I first read it, I thought it implied that shift in list context returns them all and empties the list; that is, gives the same value as %params= @_; but also empties out @_.

      After reading Abagail's explaination, I see that he's passing a hashref as the first parameter, rather than passing names/values as individual parameters.

        And you don't want to. It means the same as
        %params = %shift;
        What you might want to see instead is:
        %params = %{+shift};

        Abigail

Re: Passing by Named Parameter
by Abigail-II (Bishop) on Jan 26, 2004 at 22:14 UTC
    One could always (mis)use one of the Getopt:: modules.

    Abigail

Re: Passing by Named Parameter
by zuqif (Hermit) on Jan 27, 2004 at 13:46 UTC
    This sounds to me something suited to a relatively new methodology I heard of :
    Aspect Oriented Programming ..
    There are various PM nodes already available through Super Search, and although my grok is limited, it would allow you to dynamically cross-cut your methods with parameter parsing/validation/trapping .. good luck!

    the 'qif;
      I think that's a stretch here. Aspect-oriented programming has the property of adding certain pre-and-post functions to certain functions (and/or identifiers) based on their names, at least from my understanding. What you would end up with, then, is code that is much harder to debug, as spelling somehow now conveys semantics. Though some might think looping through keys (above) isn't elegant enough, I still think it's better than playing aspect oriented games with the symbol table/reflection/whatever-your-language-calls-it.
        You are of course absolutely right, especially if one isn't lazy enough to get a return from such OO extensions .. but it was intended simply in the spirit of tmt1wtdi - and as a knowledge share.
        It seems sad to me that the lead in AOP technique and standards is being driven by the large IDE players, JBoss et al.

        That said, I continue to generate 90% of my OO module structure & stubs from templates and XML through my own home-rolled smoke screens ..

        the 'qif;
Re: Passing by Named Parameter
by jmanning2k (Pilgrim) on Jan 28, 2004 at 18:07 UTC
    A really good example can be found in the Bio::Root::RootI module in BioPerl. See the _rearrange function.

    This is a helper function used to sort out and make named parameters consistent. It's used as follows:

    sub function { my $self = shift; my ($arg1, $arg2, $arg3) = $self->_rearrange([qw(NAME1 NAME2 NAME3)] +,@_); } function( '-name1' => 'value1', '-name2' => 'value2');
    It simply avoids repeating the parsing, uppercasing, and dash removal in each function.

    ~J

Re: Passing by Named Parameter
by snowhare (Friar) on Jan 29, 2004 at 03:31 UTC
    There are multiple modules on CPAN for doing this - including my own 'Class::ParmList' module, which can in fact handle what you asked above. Ex.
    use Class::ParmList qw (parse_parms simple_parms); sub something { my ($file, $other) = simple_parms(['-file','-other'],@_); } sub otherthing { my $parms = parse_parms({ -parms => \@_, -legal => [qw (-textcolor -border -cellpadding)], -required => [qw (-bgcolor)], -defaults => { -bgcolor => "#ffffff", -textcolor => "#000000" } }); if (not $parms) { my $error_message = Class::ParmList->error; die ($error_message); } my ($text_color) = $parms->get('-textcolor'); }