tags:

views:

102

answers:

3
#!/usr/bin/perl

use strict;
use warnings;

my @a = qw/a b c/;
(@a) x= 3;
print join(", ", @a), "\n";

I would expect the code above to print "a, b, c, a, b, c, a, b, c\n", but instead it dies with the message:

Can't modify private array in repeat (x) at z.pl line 7, near "3;"

This seems odd because the X <op>= Y are documented as being equivalent to X = X <op> Y, and the following code works as I expect it to:

#!/usr/bin/perl

use strict;
use warnings;

my @a = qw/a b c/;
(@a) = (@a) x 3;
print join(", ", @a), "\n";

Is this a bug in Perl or am I misunderstanding what should happen here?

A: 

The problem is that you're trying to modify @a in place, which Perl evidently doesn't allow you to do. Your second example is doing something subtly different, which is to create a new array that consists of @a repeated three times, then overwriting @a with that value.

Arguably the first form should be transparently translated to the second form, but that isn't what actually happens. You could consider this a bug... file it in the appropriate places and see what happens.

JSBangs
+1  A: 

My guess is that Perl is not a language with full symbolic transformations. It tries to figure out what you mean. If you "list-ify" @a, by putting it in parens, it sort of loses what you wanted to assign it to.

Notice that this does not do what we want:

 my @b = @a x 3; # we'll get scalar( @a ) --> '3' x 3 --> '333'

But, this does:

my @b = ( @a ) x 3;

As does:

( @a ) = ( @a ) x 3;

So it seems that when the expression actally appears on both sides Perl interprets them in different contexts. It knows that we're assigning something, so it tries to find out what we're assigning to.

I'd chalk it up to a bug, from a very seldom used syntax.

Axeman
Yeah, I only ran into it because I am documenting all of the operators (github.com/cowens/perlopref) and I was testing my naïve assumption of what it should do.
Chas. Owens
I don't think I'd call it a bug, since the Perl 4 manual explicitly mentions it only works on scalars: http://www.cs.cmu.edu/afs/cs.cmu.edu/Web/People/rgs/pl-exp-op.html#x
mercator
@mercator That is not the Perl manual (which would either be the Camel or something accessible from `perldoc`). The fact that it documents the same behavior does not mean the behavior is not a bug. See http://perldoc.perl.org/perlop.html#Assignment-Operators for the current (5.10.1) documentation.
Chas. Owens
@mercator Hmm, that does seem to be a proto-perldoc man page from Perl 4. Well, given that it was an error before, fixing it so that it works now will not cause an regressions and it is *not* documented as only working on scalars in Perl 5.
Chas. Owens
@Chas. Owens Actually, there's a recent, open bug report to clarify the list assignment documentation: http://rt.perl.org/rt3/Public/Bug/Display.html?id=68312. The changes it proposes clarify the only assignment that works in list context is the "ordinary" assignment: "These other assignment operators are all scalar assignment operators."
mercator
@mercator I just suggested a similar change on P5P. I don't really care if `(@a) x= 3;` works or not. Given that I have never needed it in 10 years of Perl programming I don't think it is very needed, but the lack of (modern) documentation worries me.
Chas. Owens
+4  A: 

My first thought was that it was a misunderstanding of some subtlety on Perl's part, namely that the parens around @a made it parse as an attempt to assign to a list. (The list itself, not normal list assignment.) That conclusion seems to be supported by perldiag:

Can't modify %s in %s

(F) You aren't allowed to assign to the item indicated, or otherwise try to change it, such as with an auto-increment.

Apparently that's not the case, though. If it were this should have the same error:

($x) x= 3;  # ok

More conclusively, this gives the same error:

@a x= 3;  # Can't modify private array in repeat...

Ergo, definitely a bug. File it.

Michael Carman