I’m looking for perl docs confirming the following behaviour, where passed @_ still holds the first argument, despite shift() which alters @_.
use Data::Dumper;
sub f { shift->(@_) }
f(sub{ print Dumper @_ }, 1,2);
output
$VAR1 = [
sub { "DUMMY" },
1,
2
];
4
You are asking about the order in which the shift
is performed relative to creation of the argument list.
It’s not documented.
Rely on this at your own risk, but Perl always evaluates the left of the ->
last in a call (so that the code reference is topmost on the stack).
In the following, you can see that @_
(lines 3 and 4) is evaluated before shift
(line 5).
$ perl -MO=Concise,-exec,f -e'
use Data::Dumper;
sub f { shift->(@_) }
f(sub{ print Dumper @_ }, 1,2);
'
main::f:
1 <;> nextstate(main 1266 -e:3) v
2 <0> pushmark s
3 <#> gv[*_] s
4 <1> rv2av[t2] lKM/1
5 <0> shift s*
6 <1> entersub[t3] KS/TARG
7 <1> leavesub[1 ref] K/REFC,1
-e syntax OK
Let’s walk through it.
- Let’s say that
@_
initially contains scalars A, B and C. - Line 2 marks the stack.
- Lines 3 and 4 places scalars A, B and C on the stack.
- Line 5 then places A on the stack. (It now contains four scalars since the mark.) It also shifts the contents of
@_
so that it now contains B and C. - Line 6 calls the sub identified by the topmost scalar on the stack, A. The remaining scalars added since the mark (A, B and C) are provided to the sub as arguments.
Solution: Avoid reading and modifying the same variable in the same expression.
sub f { $_[0]->( @_[ 1 .. $#_ ] ) }
sub f { my $cb = shift; $cb->( @_ ) }
sub f { my $cb = shift; &$cb }
sub f { &{ +shift } }
Or if you want to remove the stack frame for f
from the stack (hiding it from caller
and croak
), you can use the following:
sub f { my $cb = shift; goto &$cb }
12
Any expression passed to the sub is first processed, at the point of call (before the call happens, which is where the resulting @_
itself is processed). Thus this is when print
runs. So this really goes before the actual @_
is constructed and thus it could have not been changed yet.
This is perhaps our “normal” sub call behavior so I don’t know that it would be in docs specifically.
A command-line (“one-liner”) example:
perl -wE'sub tt { shift(@_); say "@_" }; tt(1, 2, my $x = print "hi")'
This prints
hi2 1
(the 1
is for success of print
)
The expression in the question defines an anonymous sub but that print
still has to run.
2
One of the answers says, that “Perl always evaluates the left of the -> last in a call”. This seems to be false for object methods.
While both
sub f { shift->(@_) }
f(sub{ print 'f ' . Data::Dumper::Dumper @_ });
sub g { $_[0]->(@_) }
g(sub{ print 'g ' . Data::Dumper::Dumper @_ });
lead to equivalent output
f $VAR1 = [
sub { "DUMMY" }
];
g $VAR1 = [
sub { "DUMMY" }
];
the following
package F {
sub f {shift->ff(@_)}
sub ff {print 'F ' . Data::Dumper::Dumper @_}
}
package G {
sub g {$_[0]->gg(@_)}
sub gg {print 'G ' . Data::Dumper::Dumper @_}
}
(bless {}, 'F')->f;
(bless {}, 'G')->g;
does not
F $VAR1 = [
bless( {}, 'F' )
];
G $VAR1 = [
bless( {}, 'G' ),
$VAR1->[0]
];
0