http://www.perlmonks.org?node_id=156497

vek has asked for the wisdom of the Perl Monks concerning the following question:

Esteemed monks, I seek your wisdom.

If you have a lot of arguments that you need to pass to a subroutine, is it preferrable to pass & access them individually:
do_stuff($foo, $bar, $foo_too, $bar_too, $foo_who_poo); sub do_stuff { my ($foo, $bar, $foo_too, $bar_too, $foo_who_poo) = @_; }
Or pass & access by named parameter:
do_stuff(one=>$foo, two=>$bar, three=>$foo_too, four=>$foo_who_poo); sub do_stuff { my %args = @_; }
Or is it purely personal preference?

I'm starting to get some 'feature creep' in my code and keep adding subroutine arguments as the specifications change. I'm thinking at the very least if I pass by named parameter I'll have some 'self-documenting' code.

What do you do?

Replies are listed 'Best First'.
Re: Accessing Subroutine Arguments
by Chmrr (Vicar) on Apr 04, 2002 at 00:07 UTC

    Since this is the realm of coding style, it's almost certainly all personal preference. ;>

    I know that I tend to prefer using a hash once I get above three or four parameters. Some of the reasons that I can think of why I tend to do so:

    • I don't have to remember the order in which the parameters are passed. Granted, I do have to remember the names of the keys, but I think that's a small sacrifice. One can also use the same two or three key names for multiple subroutines ot make it easier to remember.
    • Self-documentation. As you hinted at, to me it's clearer what:load_foo(filename=>"bar",template="baz", debug=>1); is doing than: load_foo("bar","baz",1); does.
    • It means I can establish defaults for some parameters rather easily. To use the same fictitious load_foo subroutine:
      sub load_foo { my %args = ( # Defaults template => "zort", debug => 0, @_); ... # yadda yadda yadda }
    • No reordering. If you're using lots of individual scalars, and you find out you don't need the third our of seven of them -- you'd better make sure you take that one out of all of the places where you call it, or you're up a creek. With a hash, it's not so much of an issue.

    *shrug* All of the above are just personal preferences, yes. Do what seems to work best for you.

    perl -pe '"I lo*`+$^X$\"$]!$/"=~m%(.*)%s;$_=$1;y^`+*^e v^#$&V"+@( NO CARRIER'

(Ovid) Re: Accessing Subroutine Arguments
by Ovid (Cardinal) on Apr 04, 2002 at 00:32 UTC

    The fewer parameters that you pass to a subroutine, the easier it tends to be to maintain (assuming that everything is nicely encapsulated and you're not using globals or something like that). This is because the logic of dealing with one variable is typically going to be simpler than the logic of dealing with multiple variables. However, if I must pass numerous parameters, I like to set a threshhold of say, 4 parameters, and switch to a named argument style.

    Consider the following, which is easy to happen when reusing a lot of code:

    some_func( $foo, $bar, $baz, $qux ); # in another program some_func( $w, $x, $y, $z ); # yet another program some_func( $amount, $rate, $limit, $exception );

    With this, not only are you forced to pass multiple parameters (even if they are undef), but you have to remember the order of the parameters. This can be awkward. Hmm... do I have the third and fourth parameters reversed, or not? However, what if $limit is optional, and I can't remember the order of $rate and $exception? With named parameters, this is a moot point:

    some_func( exception => $exception, amount => $amount, rate => $rate );

    Cheers,
    Ovid

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

Re: Accessing Subroutine Arguments
by Bobcat (Scribe) on Apr 04, 2002 at 02:10 UTC
    I actually prefer to use the "named parameter" method. Not only does it lead to more readable code, but you can do neat things with defaults this way.
    use strict; use Data::Dumper; sub foo { my %opt = ( text => 'Default Text' , style => 'Default Style' , bar => 'belch' ); %opt = ( %opt, @_ ); print Dumper \%opt; } foo(); # Options stay as specified above. foo( bar => 'yuk' ); # $opt{text} and $opt{style} stay the same, # $opt{bar} becomes 'yuk' instead of 'belch'.
    I find this really handy for functions that can have a lot of obscure parameters that you only want to change once in a blue moon.
Re: Accessing Subroutine Arguments
by perrin (Chancellor) on Apr 04, 2002 at 00:30 UTC
    At eToys we made named parameters part of our coding conventions. It really helped, and I would recommend it as a good way to help keep stupid bugs out of your code.
      Just wondering, do you use named params when a sub takes, say, only two params?
        If a sub takes only two params, and is not part of the public interface, it's a judgement call based on the likelihood of that sub growing in the future. If it's part of the public interface, I would used named params even if there was only a single one. It's insurance against changing a bunch of other classes later on.
Re: Accessing Subroutine Arguments
by lshatzer (Friar) on Apr 04, 2002 at 00:56 UTC

    In my progress as a perl programmer, I started out with your first method, passing in a list of variables, and then grabbing them in that order in the subroutine.

    As it got more advanced, I started using the second, (usually with a hash reference or something similar like you posted).

    Then in some cases, where I keep passing the same value, since it does not change much, I found creating an object, blessing it, and making methods to do the work was a little easier to do.

Re: Accessing Subroutine Arguments
by rjray (Chaplain) on Apr 04, 2002 at 00:38 UTC

    As the previous respondent pointed out, this is more in the area of personal style preference, but I also agree with him that the nature of the situation you describe lends itself to the hash-table/named-args approach.

    When I write subroutines, I give some time to consider (a) the scope of the routine itself (not in the lexical sense, but in the sense of, "How big and significant is this likely to become?"), and (b) easy will it be to document clearly for the sake of other users? For that matter, the "other users" question can often be a deciding point-- one utility-script I package with RPC::XML uses long lists of parameters between routines, but no one else is going to use that routine independantly, it will only ever be part of the utility script (which harkens back to the "scope of use" question).

    I realize this is no more a definative answer than the previous post was, because this is not an absolute cut-and-dried situation. However, just for arguments' sake, I'll say that in my opinion you should go with the hash-table approach for the situation you specifically describe.

    --rjray

Re: Accessing Subroutine Arguments
by Juerd (Abbot) on Apr 04, 2002 at 07:14 UTC

    Or is it purely personal preference?

    It is. I start using named arguments when a sub has more than two.

    If does require that your sub names are logical, though.

    readfile('foo.txt');
    is already self-documenting, while
    readfile( file => 'foo.txt' );
    is redundant.

    U28geW91IGNhbiBhbGwgcm90MTMgY
    W5kIHBhY2soKS4gQnV0IGRvIHlvdS
    ByZWNvZ25pc2UgQmFzZTY0IHdoZW4
    geW91IHNlZSBpdD8gIC0tIEp1ZXJk
    

Re: Accessing Subroutine Arguments
by trs80 (Priest) on Apr 04, 2002 at 14:20 UTC
    For me passing a hash(ref) makes my life easier, especially in the earlier stages of development when I don't even know for sure what options may need to be available to a method/sub or in the (un)likely event that the requirements should change you have more flexibiltiy.