Given the perennial discussions regarding using named arguments versus ordered arguments, I whipped out a short module that allows you to use named arguments with any subroutine that uses ordered arguments. Comments and/or suggestions welcome (particularly re: the namespace). It doesn't seem worthwhile to upload to the CPAN, but I also didn't see any modules on the CPAN that appear to do this. Is it too trivial?

package Sub::Hashwrap; use 5.00500; use strict; use Carp qw/croak/; require Exporter; use vars qw/ $VERSION @ISA @EXPORT_OK /; $VERSION = '.01'; @ISA = qw/ Exporter/; @EXPORT_OK = qw/ wrap /; sub wrap { my %wrapper = @_; foreach ( qw/ sub names / ) { if ( ! exists $wrapper{$_} ) { croak "You must supply '$_' in the argument list'"; } } croak "'sub' value must not be a reference." unless ! ref $wrapper +{ sub }; my $sub = $wrapper{ sub }; if ( $sub !~ /::/ ) { # if it's not fully qualified, append the package $sub = caller(0)."::$sub"; } no strict 'refs'; my $orig_sub; $orig_sub = \&$sub; $wrapper{ hashref } = 1 if ! exists $wrapper{ hashref }; $wrapper{ default } = {} if ! exists $wrapper{ default }; *{$sub} = sub { my %args = $wrapper{ hashref } ? %{$_[0]} : @_; my @orig_args; foreach my $arg_name ( @{$wrapper{ names }} ) { if ( exists $args{ $arg_name } ) { push @orig_args, $args{ $arg_name }; } elsif ( exists $wrapper{ default }{ $arg_name } ) { push @orig_args, $wrapper{ default }{ $arg_name }; } else { croak( "Cannot find value or default for '$arg_name'" +); } } return $orig_sub->( @orig_args ); } } 1; __END__ =head1 NAME Sub::Hashwrap - Perl extension for using named arguments with any sub =head1 SYNOPSIS use Sub::Hashwrap qw/wrap/; wrap ( sub => some_sub_name, names => [qw/ names for your arguments /], default => { your => 1, arguments => undef } ); some_sub_name( { names => [qw/Bill Mary Ovid/], for => '??' } ); =head1 DESCRIPTION Sometimes it can be a pain to work with a sub that takes a long list o +f arguments. Trying to remember their order can be annoying, but it ge +ts worse if some of the arguments are optional. This module allows you to use +named arguments with any subroutine. It has only one function, C<Sub::Hashwrap::wrap>, which has two mandatory and two optional argum +ents. =head2 Parameters =over 4 =item C<sub> Required. This argument is the name of the sub (B<not> a reference to + it. If just the sub name is provided, the calling package is assumed to be th +e correct one. Otherwise, a fully-qualified sub name may be used. This will use the calling package. wrap( sub => 'process_report', names => [qw/ report summary totals /] ); This will use C<Some::Package::>. wrap( sub => 'Some::Package::process_report', names => [qw/ report summary totals /] ); =item C<names> Required. This should be an array ref with the names of the arguments + in the order in which they are supplied to the sub. See examples for C<sub>. =item C<hashref> Optional. If you would rather supply a list instead of a hashref, set + this to false. The default is true. =item C<default> Optional. This is a hashref with default values for any argument that + you don't supply when you call the subroutine. =back =head2 Example use Data::Dumper; use Sub::Hashwrap qw/wrap/; wrap( sub => foo, names => [qw/ first second third /], hashref => 1, default => { second => 'deux' } ); foo( {first => 1, third => 3} ); sub foo { print Dumper \@_; } =head2 EXPORT None by default. Adding C<wrap> to the import list will import it. =head1 AUTHOR Copyright 2002, Curtis "Ovid" Poe E<lt>poec@yahoo.comE<gt>. All right +s reserved. This library is free software; you can redistribute it and/or modify i +t under the same terms as Perl itself. =head1 SEE ALSO L<perl>. =cut

Cheers,
Ovid

Update: This is now on the CPAN as Sub::NamedParams. It now has an optional target that you can specify. The target is the new sub name taking named arguments, while still allowing the old sub name to be called with ordered arguments. This is useful in a collaborative environment where other programmers may still be using the sub in question.

Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Replies are listed 'Best First'.
Re: RFC: Sub::Hashwrap - Use named arguments with any sub
by George_Sherston (Vicar) on Apr 27, 2002 at 00:26 UTC
    I like this a lot, and I'm using it tomorrow (well, Monday). I often want to do this, but I also quite often want to have the flexibility to call the sub with either a hash or an array - for example when I have a sub which sometimes gets by on just one argument, or sometimes needs a whole load of stuff specified. So I favour the following tweak-ette (which I agree is making a boldish assumption that the user wd have to be aware of, namely that if *all* the named args are missing, that's not because we completely ballsed it up, but because we're submitting an array).
    *{$sub} = sub { my %args = $wrapper{ hashref } ? %{$_[0]} : @_; my @orig_args; my @missing_args; foreach my $arg_name ( @{$wrapper{ names }} ) { if ( exists $args{ $arg_name } ) { push @orig_args, $args{ $arg_name }; } elsif ( exists $wrapper{ default }{ $arg_name } ) { push @orig_args, $wrapper{ default }{ $arg_name }; } else { push @missing_args, $arg_name; } } if (@missing_args == 0) { return $orig_sub->( @orig_args ); } elsif (@missing_args == @{$wrapper{ names }}) { return $orig_sub->( @_ ); } else { croak("Cannot find value or default for arg(s) '" . (join +"', ", @missing_args) . "'"); } }
    My only other observation is that it raises a "Subroutine redifined" warning - my grasp of name space issues is too weak to know whether that can be avoided. But I must say, I like it.

    § George Sherston
Re: RFC: Sub::Hashwrap - Use named arguments with any sub
by rnahi (Curate) on Apr 27, 2002 at 09:16 UTC
    Well done, Ovid!
    IMHO, it should be in the CPAN, no doubt, and for two reasons:
    1. It is useful per se, because it will help to make my scrips more clear.
    2. It is a documentation aid, since it will give a clear indication of the parameters to be given. Prepending a call to wrap before each function will clarify which parameters to use, without looking at all the sub's code. For this reason, I would change "names" into "parameters", but it's just a personal thing.
    However, I have some questions about the module implementation:
    • What about the overhead? I am not too well versed in Perl internals, but I have the feeling that this wrap is going to cost my subs a dear price in performance.
    • For completeness, I would add a check for unwanted parameters, i.e. if I call the sub with a named parameter that was not defined, I should get a warning. I know that it will generate even more overhead, but there are cases where precision is more important than speed. Is it feasible?

Re: RFC: Sub::Hashwrap - Use named arguments with any sub
by samtregar (Abbot) on Apr 27, 2002 at 03:14 UTC
    Neat! I think this definitely belongs on CPAN. But you're right, the name needs work. When I first read it I assumed it provided some kind of wrapper around a hash. Maybe Devel::UseNamedParams? Or Params::NamedWrapper? Hmm, neither is great. More thought required...

    -sam