Is 'shift' evil for processing Perl subroutine parameters?

Nikolai Prokoschenko picture Nikolai Prokoschenko · Aug 25, 2009 · Viewed 15k times · Source

I'm frequently using shift to unpack function parameters:

sub my_sub {
    my $self = shift;
    my $params = shift;
    ....
}

However, many on my colleagues are preaching that shift is actually evil. Could you explain why I should prefer

sub my_sub {
    my ($self, $params) = @_;
    ....
}

to shift?

Answer

John Siracusa picture John Siracusa · Aug 25, 2009

The use of shift to unpack arguments is not evil. It's a common convention and may be the fastest way to process arguments (depending on how many there are and how they're passed). Here's one example of a somewhat common scenario where that's the case: a simple accessor.

use Benchmark qw(cmpthese);

sub Foo::x_shift { shift->{'a'} }
sub Foo::x_ref   { $_[0]->{'a'} }
sub Foo::x_copy  { my $s = $_[0]; $s->{'a'} }

our $o = bless {a => 123}, 'Foo';

cmpthese(-2, { x_shift => sub { $o->x_shift },
               x_ref   => sub { $o->x_ref   },
               x_copy  => sub { $o->x_copy  }, });

The results on perl 5.8.8 on my machine:

            Rate  x_copy   x_ref x_shift
x_copy  772761/s      --    -12%    -19%
x_ref   877709/s     14%      --     -8%
x_shift 949792/s     23%      8%      --

Not dramatic, but there it is. Always test your scenario on your version of perl on your target hardware to find out for sure.

shift is also useful in cases where you want to shift off the invocant and then call a SUPER:: method, passing the remaining @_ as-is.

sub my_method
{
  my $self = shift;
  ...
  return $self->SUPER::my_method(@_);
}

If I had a very long series of my $foo = shift; operations at the top of a function, however, I might consider using a mass copy from @_ instead. But in general, if you have a function or method that takes more than a handful of arguments, using named parameters (i.e., catching all of @_ in a %args hash or expecting a single hash reference argument) is a much better approach.