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
?
Assigning @_ to a list can bring some helpul addtional features.
It makes it slightly easier to add additional named parameters at a later date, as you modify your code Some people consider this a feature, similar to how finishing a list or a hash with a trailing ',' makes it slightly easier to append members in the future.
If you're in the habit of using this idiom, then shifting the arguments might seem harmful, because if you edit the code to add an extra argument, you could end up with a subtle bug, if you don't pay attention.
e.g.
later edited to
however
If another colleague, who uses the first form dogmatically (perhaps they think shift is evil) edits your file and absent-mindedly updates this to read
and they may have introduced a subtle bug.
Assigning to @_ can be more compact and efficient with vertical space, when you have a small number of parameters to assign at once. It also allows for you to supply default arguments if you're using the hash style of named function parameters
e.g.
I would still consider it a question of style / taste. I think the most important thing is to apply one style or the other with consistency, obeying the principle of least surprise.
It is not evil, it is a taste sort of thing. You will often see the styles used together:
I tend to use
shift
when there is one argument or when I want to treat the first few arguments differently than the rest.Perl::Critic is your friend here. It follows the the "standards" set up in Damian Conway's book Perl Best Practices. Running it with --verbose 11 gives you an explanation on why things are bad. Not unpacking @_ first in your subs is a severity 4 (out of 5). E.g:
This is, as others have said, a matter of taste.
I generally prefer to shift my parameters into lexicals because it gives me a standard place to declare a group of variables that will be used in the subroutine. The extra verbosity gives me a nice place to hang comments. It also makes it easy to provide default values in a tidy fashion.
If you are really concerned about the speed of calling your routines, don't unpack your arguments at all--access them directly using
@_
. Be careful, those are references to the caller's data you are working with. This is a common idiom in POE. POE provides a bunch of constants that you use to get positional parameters by name:Now the big stupid bug you can get if you habitually unpack params with shift is this one:
I think that consistent code style is more important than any particular style. If all my coworkers used the list assignment style, I'd use it too.
If my coworkers said there was a big problem using shift to unpack, I'd ask for a demonstration of why it is bad. If the case is solid, then I'd learn something. If the case is bogus, I could then refute it and help stop the spread of anti-knowledge. Then I'd suggest that we determine a standard method and follow it for future code. I might even try to set up a Perl::Critic policy to check for the decided upon standard.
It isn't intrinsically evil, but using it to pull off the arguments of a subroutine one by one is comparatively slow and requires a greater number of lines of code.
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.The results on perl 5.8.8 on my machine:
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 aSUPER::
method, passing the remaining@_
as-is.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.