What's the benefit of shifting @_ for argument passing in Perl?

arguments, arrays, function, perl, shift

Solution

I agree with all points. But the basic question remains: Why should I shift instead of doing a list assignment or a sequence of scalar assignments?

Since I do shifting, I'll explain why I do it.

There are three may ways you can handle parameters to a subroutine:

Method 1: List Assignment

 sub foo {
    my ( $user, $date, $system ) = @_;   # No shifting, and @_ preserved

Method 2: Individual Assignment from @_

 sub foo {
    my $user   = $_[1];  
    my $date   = $_[2];   # Must be YYYY-MM-DD
    my $system = $_[3];   # Optional

Method 3: Using "shift"

sub foo {
    my $user    = shift;
    my $date    = shift;    # Must be YYYY-MM-DD
    my $system  = shift;    # Optional

Method 1 is popular. Ikegami uses it, and so do many other advanced Perl developers. It gives you a parameter list and gives you a way of saying these are my parameters and no others.

However, Method 2 and Method 3 give you a nice, easy to read parameter list, and you can add comments to the end of the line. However, Method 2 also has the advantage of maintaining the value of `@_`, so why use `shift`?

Take a look at Method 2 again. See a problem? I started with `@_[1]` and not with `@_[0]` -- a common error. Plus, as you're developing your subroutine, you might decide to reorder your parameters. Using Method 2 means renumbering them. Method 3 doesn't require renumbering, so you don't end up with this:

 sub foo {
    my $date   = $_[1];   # Must be YYYY-MM-DD
    my $user   = $_[0];  
    my $system = $_[2];   # Optional

Um, what's the order of the parameters again?

So, what about preserving the value of `@_`? If you write a program where you change the value of a parameter, you're probably aren't going to be restoring it from `@_`. And, if you do, you are likely to produce some error prone code. If you need to munge a parameter, put it in another variable and munge that. This isn't 1975 where computers had only 4 kilobytes of memory.

And, talking about the 1970s, computers are fast enough that the amount of time of a few (dozen, hundred, thousands) shift operations isn't going to make that much difference in your total runtime. If your program is inefficient, optimize it where it is inefficient, and not by possibly shaving a few milliseconds eliminating a shift. More time will be spent maintaining your program than actually running it.

Damian Conway (The author of Perl Best Practices) recommends both Method #1 and Method #3. He states that Method #1 is "...is more concise, and it keeps the parameters together in a horizontal list, which enhances readability, provided that the number of parameters is small." (the emphasis is mine).

Damian states about Method #3: "The shift-based version is preferable, though, whenever one or more arguments has to be sanity-checked or needs to be documented with a trailing comment."

I simply use Method #3 all of the time. This way, I don't have to worry about reformatting if my parameter list grows, and I simply think it looks better.

Problem

``` sub foo { $arg1 = shift @_; $arg2 = shift @_; # ... } ``` What's the benefit of this idiom? I see only disadvantages compared to explicitly working with `$_[0]`, `$_[1]`, ... The array has to be shifted, which is time consuming. It is destroyed, so that at a later point in time, the arguments have vanished (sad if you need them again and have overwritten your $arg1 with a different value).

Original source