krunchky has asked for the wisdom of the Perl Monks concerning the following question:

Greetings monks,

The code chop foreach qw/foo bar baz/ will die with 'Modification of a read-only value attempted'. This is good.

However, if I make my own chop function and run it on the that literal list like so, printing what's actually getting chopped:
sub mychop(\$) { chop ${shift()} } foreach my $thing (qw/foo bar baz/) { print "$thing -> "; my $c = mychop $thing; print " $thing,$c\n"; }
Instead of dying with 'read only value' as I would expect, it prints:
foo -> foo,o bar -> bar,r baz -> baz,z
Can anyone enlighten me as to why this is?

Replies are listed 'Best First'.
Re: foreach funny business
by Sidhekin (Priest) on Jun 29, 2006 at 19:35 UTC

    A bug in your version of perl, perhaps? On mine, it dies:

    sidhekin@blackbox:~$ perl -v This is perl, v5.8.7 built for i486-linux Copyright 1987-2005, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using `man perl' or `perldoc perl'. If you have access to + the Internet, point your browser at http://www.perl.org/, the Perl Home Pa +ge. sidhekin@blackbox:~$ perl sub mychop(\$) { chop ${shift()} } foreach my $thing (qw/foo bar baz/) { print "$thing -> "; my $c = mychop $thing; print " $thing,$c\n"; } __END__ Modification of a read-only value attempted at - line 1. foo ->

    Update: After some trial-and-error, it seems to be the case that, on threaded builds, trying to take a reference to an alias of a constant string creates a copy of that constant string and results in a reference to that copy, while on non-threaded builds, you get a reference to the constant itself.

    # v5.8.3 built for i386-linux-thread-multi: -bash-2.05b$ perl -le 'print map{chop$$_, $_}\$_, \$_, \$_ for "abc", +($x="abc")' cSCALAR(0x804cbb8)cSCALAR(0x804cc78)cSCALAR(0x804c99c) cSCALAR(0x805f69c)bSCALAR(0x805f69c)aSCALAR(0x805f69c) # v5.8.7 built for i486-linux: sidhekin@blackbox:~$ perl -le 'print map{chop$$_, $_}\$_, \$_, \$_ for + "abc", ($x="abc")' Modification of a read-only value attempted at -e line 1. Attempt to free unreferenced scalar: SV 0x812ec2c.

    Whether this is a bug or undefined behaviour, though ... I'll leave that for others to decide.

    print "Just another Perl ${\(trickster and hacker)},"
    The Sidhekin proves Sidhe did it!

      Bug or undefined behaviour? FWIW ive seen this issue with DDS in all kinds of places. It seems to be OS and version dependent. The question comes down to whether shift returns an alias to the item shifted out or if it returns a copy. Similar is whether \\$foo returns a reference to a readonly value or not.

      Personally i think that the "reasonable" behaviour is to return a copy as that is the most DWIM behaviour.

      ---
      $world=~s/war/peace/g

      Interesting. I'm running debian unstable, which has this perl right now:
      rob@toblerone:~$ perl -v This is perl, v5.8.8 built for i486-linux-gnu-thread-multi Copyright 1987-2006, Larry Wall etc, etc....
Re: foreach funny business
by Hue-Bond (Priest) on Jun 30, 2006 at 00:16 UTC

    I just took the time to do some testing on several machines. All these accepted to modify the value:

    $ perl -le'sub mychop(\$) { chop ${shift()} } print mychop $_ foreach +(qw/foo/);' o $ perl -le'sub mychop(\$) { chop ${$_[0]} } print mychop $_ foreach (q +w/foo/);' o This is perl, version 5.005_02 built for PA-RISC1.1 - HP-UX td192 B.11 +.11 This is perl, v5.8.3 built for i586-linux-thread-multi - SUSE LINUX En +terprise Server 9 This is perl, v5.8.3 built for IA64.ARCHREV_0-thread-multi - HP-UX td1 +64 B.11.23 This is perl, v5.8.3 built for x86_64-linux-thread-multi - SUSE LINUX +Enterprise Server 9 This is perl, v5.8.4 built for alpha-linux-thread-multi - Debian/Linux + 3.1 This is perl, v5.8.4 built for i386-linux-thread-multi - Debian/Linux +3.1 This is perl, v5.8.5 built for i386-linux-thread-multi - Red Hat Enter +prise Linux AS release 4 This is perl, v5.8.5 built for x86_64-linux-thread-multi - Red Hat Ent +erprise Linux AS release 4 This is perl, v5.8.8 built for hppa-linux-gnu-thread-multi - Debian/Li +nux 3.1

    While these refused to:

    > perl -le'sub mychop(\$) { chop ${shift()} } print mychop $_ foreach +(qw/foo/);' Modification of a read-only value attempted at -e line 1. > perl -le'sub mychop(\$) { chop ${$_[0]} } print mychop $_ foreach (q +w/foo/);' Modification of a read-only value attempted at -e line 1. This is perl, v5.8.0 built for alpha-dec_osf - Compaq Tru64 UNIX V5.1B This is perl, v5.8.0 built for i386-netbsd - NetBSD 3.0 This is perl, v5.8.6 built for alpha-freebsd - FreeBSD 6.0-RELEASE This is perl, v5.8.6 built for i386-freebsd-64int - FreeBSD 5.4-RELEAS +E

    Update: I added the operating system by hand, it's not part of the perl -v output on any system.

    --
    David Serrano

Re: foreach funny business
by shmem (Chancellor) on Jun 29, 2006 at 19:57 UTC
    Consider
    #1 sub mychop(\$) { chop ${shift()} }
    versus
    # 2 sub mychop(\$) { chop ${$_[0]} }
    As perl v5.8.8, the program dies with mychop version #2, but not with version #1.

    ${shift()} copies (via shift()) $_[0] into a ref-thingy, which is then dereferenced with $ - a behaviour which I deem correct. I would take a die() with #1 as a bug. At version #2 the die() is ok - the reference $_[0] is wrapped into a reference and dereferenced - still $_[0], thus read-only.

    mhm.. really? oh, wait...

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      Running your #2 on my perl does not die, and mine claims to be 5.8.8. Are you sure you're on your rocker? :-)

      Anyway, to my way of thinking the shift() shouldn't be giving you anything other than _exactly_ what was in $_[0], not some copy.

      Now who's with me? /me hoists pitchfork and torch
        I'm with you, you're right, I'm not. And then, again you're right - 5.8.8 doesn't die, I copied from the wrong shell, and shift() should just pass $_[0], not a copy, unless somebody made shift ro-aware. Have to look after this...

        Mhm. Scanning perldelta... nothing. Then, it's a bug. Or it's just undefined behaviour?

        But hey, it's a clever one. It's like, say, putting your hand in the band-saw, and get a finger cut, but then you draw out your hand and it's ok because it's read-only. One finger extra ;-)

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: foreach funny business
by ikegami (Patriarch) on Jun 29, 2006 at 20:32 UTC

    Creating a reference to a constant must make a copy of it, because the following works without error too: (Perl v5.6.1)

    foreach my $thing (qw/foo bar baz/) { print "$thing -> "; my $c = chop ${\$thing}; print " $thing,$c\n"; }
      My 5.6.1 does die:
      perseus [gm] 22:26 /home/gm > perl -v This is perl, v5.6.1 built for i386-openbsd Copyright 1987-2001, Larry Wall Perl may be copied only under the terms of either the Artistic License + or the GNU General Public License, which may be found in the Perl 5 source ki +t. Complete documentation for Perl, including FAQ lists, should be found +on this system using `man perl' or `perldoc perl'. If you have access to + the Internet, point your browser at http://www.perl.com/, the Perl Home Pa +ge. perseus [gm] 22:26 /home/gm > perl foreach my $thing (qw/foo bar baz/) { print "$thing -> "; my $c = chop ${\$thing}; print " $thing,$c\n"; } Modification of a read-only value attempted at - line 3. foo -> perseus [gm] 22:26 /home/gm >

      --shmem

      _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                    /\_¯/(q    /
      ----------------------------  \__(m.====·.(_("always off the crowd"))."·
      ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      Same bug, I guess ... and it's still not present in my perl v5.8.7 built for i486-linux:

      sidhekin@blackbox:~$ perl foreach my $thing (qw/foo bar baz/) { print "$thing -> "; my $c = chop ${\$thing}; print " $thing,$c\n"; } __END__ Modification of a read-only value attempted at - line 3. foo ->

      print "Just another Perl ${\(trickster and hacker)},"
      The Sidhekin proves Sidhe did it!

      On a debian stable machine I have, I see that that code (${\$thing}) on v5.8.4 built for i386-linux-thread-multi does not die, produces the usual
      foo -> foo,o bar -> bar,r baz -> baz,z
      So, um, there's another data point.
Re: foreach funny business
by jwkrahn (Abbot) on Jun 29, 2006 at 19:51 UTC
    In mychop shift() gives you a copy of the string which you can modify.

      In mychop shift() gives you a copy of the string which you can modify.

      Nope, thanks to the prototype (\$), shift() gives you a copy of a reference to the string, which (when derefed), you really should not be able to modify.

      print "Just another Perl ${\(trickster and hacker)},"
      The Sidhekin proves Sidhe did it!

Re: foreach funny business
by girarde (Hermit) on Jun 30, 2006 at 03:42 UTC
    My naive guess was that these were barewords and hence unchoppable, but it seems I was mistaken.

    I do wonder at wanting to chop barewords instead of strings.

      They're not barewords. (It's a literal.) He knows why they are not chopable. (It's because they are read-only SVs.) He wants to know why mychop can chop those read-only SVs when chop can't. (Turns out it's pretty random how mychop works on different platforms, so there's probably no good answer to the question.)