[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