[Edinburgh-pm] a Perl surprise
Aaron Crane
perl at aaroncrane.co.uk
Wed Jul 18 08:05:30 PDT 2012
Nick <oinksocket at letterboxes.org> wrote:
> perl -MO=Deparse -le ' sub wtf($) { @_ }; @a = wtf qw(6 7 8); print @a;'
> BEGIN { $/ = "\n"; $\ = "\n"; }
> sub wtf ($) {
> @_;
> }
> @a = wtf(('???', '???', '8'));
> print @a;
>
> WTF are those '???' things? Discarded constants I suppose?
Yes — bits of the optree that can never be executed, so they were
discarded by the optimiser; but there's still a remnant of them hidden
away for Deparse to show.
> Anyway, seems that the reason for the surprise is that in this case (with a
> prototype):
>
> wtf qw(6 7 8)
>
> is equivalent to:
>
> wtf (('6', '7', '8'))
Specifically, the $ prototype character, which (because of this
gotcha) is almost never useful. (My rules of thumb for prototypes are
(a) & and &@ are occasionally useful; (b) you'll need to match the
core's prototype if you're overriding a builtin; and (c) shun other
uses of prototypes.)
> A list (as opposed to an array) in scalar context evaluates the to *last*
> element (a bit of another gotcha I happened to already know about).
There isn't really any such thing as a list in scalar context, at
least in one reasonable analysis of how all this works. Instead, this
is about the behaviour of the comma operator: in list context, the
comma operator constructs a list, while in scalar context, it discards
the operand on its left-hand side and returns the operand on its
right-hand side.
qw is defined to give you the same behaviour: it yields a list in list
context, or the last element in scalar context).
My "no list in scalar context" claim may sound like nitpicking, but it
matters for code like this:
my @x = (10 .. 12);
my $y = (6, 7, @x);
Given that lists always flatten, treating the assignment to $y as a
"list in scalar context" suggests that it would be the same as
my $y = (6, 7, 10, 11, 12);
which would set $y to 12, as you've discovered. But in fact, the
sequence of events is:
- scalar-evaluate 6, 7 (by throwing away the 6)
- scalar-evaluate 7, @x (by throwing away the 7)
- scalar-evaluate @x (by returning its length)
- scalar-assign that value (the length of @x) to my $y
so $y ends up containing 3, not 12.
> perl -le 'sub wtf { my @a = 6, 7, 8; @a }; print wtf;' # -> 6
> perl -le 'sub wtf { my $a = (6, 7, 8); $a }; print wtf;' # -> 8
I don't think your first example there shows what you think it does —
the relative precedence of assignment and comma make it identical to
perl -le 'sub wtf { (my @a = 6), 7, 8; @a } print wtf'
Parenthesising the list (or using qw, which behaves syntactically like
a parenthesised term, despite not necessarily having any parens) gives
you this:
$ perl -le 'sub wtf { my @a = (6, 7, 8); @a } print wtf'
678
--
Aaron Crane ** http://aaroncrane.co.uk/
More information about the Edinburgh-pm
mailing list