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


in reply to Re: shift vs @_
in thread shift vs @_

my $self = shift if (ref $_[0]); my($arg1,$arg2,$arg3)=@_;

Eeek! This sort of thing can rapidly get you into serious trouble. First of all, there's the fact that this won't operate properly if it's called as a class method such as Some::Package->foo( qw(arg1 arg2 arg3) ); $_[0] is a string, in that case.

Secondly, it breaks down if you pass in a normal reference as your first argument. foo({'hashkey' => 'hashval'}, qw(arg4 arg5)); You end up setting a normal arrayref as $self.

And thirdly, and potentially most importantly, if the conditional fails, then the variable maintains its scope. There are other threads on the board talking about the pitfalls of constructs like my $xyz if 0, which is what can happen here. The short answer is in this case $xyz would end up acting like a static variable. The first time, it gets initialized to something random (well, not random - it's 0 (or undef?), but that's not guaranteed and could change at any time (not that it has) ), and on subsequent calls, it maintains its value.

It doesn't directly cause problems here, probably, but observe this contrived function.

sub foo { my $self = shift if (ref $_[0]); $self++ unless ref $self; my($arg1,$arg2,$arg3)=@_; print "($self)($arg1)($arg2)($arg3)\n"; } foo(qw(a1 a2 a3)); foo(qw(a3 a4 a5)); Output: (1)(a1)(a2)(a3) (2)(a3)(a4)(a5)

Note how $self survived between calls and was around to increment. Bugs of this nature can be horrible to track down. It's also frowned upon to try to use this for static variables - use a closure or wrap the subroutine in an additional lexical scope to have "static" variables scoped to the subroutine. Besides, it's tougher to read this way. In general, it's best to always avoid my $foo if $something constructs, except for obfuscated contests or the like.

Replies are listed 'Best First'.
Re^3: shift vs @_
by sgifford (Prior) on Oct 02, 2006 at 20:29 UTC
    Thanks jimt, I've updated that example to use:
    my $self = ref $_[0] ? shift : undef;
    which doesn't seem to suffer from the largest of the problems. I've also added some clarifications about when it will and won't work.

    As an aside, you would have a very hard time convincing me that this behavior of my and conditionals is anything but a bug in Perl. :)

Re^3: shift vs @_
by jhourcle (Prior) on Oct 03, 2006 at 15:05 UTC

    First of all, there's the fact that this won't operate properly if it's called as a class method such as Some::Package->foo( qw(arg1 arg2 arg3) ); $_[0] is a string, in that case.

    Secondly, it breaks down if you pass in a normal reference as your first argument. foo({'hashkey' => 'hashval'}, qw(arg4 arg5)); You end up setting a normal arrayref as $self.

    I can get around some of those problems. First, if we assume that the subroutine was written as a function originally, and had no concept of self:

        shift if UNIVERSAL::isa ($_[0], __PACKAGE__);

    We can also deal with the possibility that we need self for the class name (eg, incase soemone calls it as a method to override inherited routines, but there's still existing code that has code that assumes it's a function:

    my $self = __PACKAGE__; $self = shift if UNIVERSAL::isa ($_[0], __PACKAGE__);

    It completely handles your first issue, however, this will break if you have a method that takes as its first argument an item of its same type (and someone tries calling it as a function), or if someone does some sort of multiple inheritance, that results in the first argument inheriting from this class (and then calls it as a function). It also adds an additional problem case where a function argument that's a string containing the name of a package that inherits the package in question is assumed to be the 'Class->method()' syntax..