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

my %guid=(218827=>5,109940=>2); BEGIN { %handle= ( cmd => sub { my $ref = sub{$guid{218827}++;warn "sub: $guid{218827}";}; $ref->(); }, ); } $handle{cmd}->();
Anyone mind explaining why this produces "Modification of a read-only value attempted at D:\home\buu\codbot\test.pl line 9."?

Replies are listed 'Best First'.
Re: modification of read-only in hash ++
by rob_au (Abbot) on Feb 05, 2004 at 09:37 UTC
    The BEGIN block is executed prior to the definition of the %guid and as such the hash element $guid{218827} is auto-vivified in this code block. The auto-vivified hash element has an undefined value which you are subsequently attempting to increment.

    For details on the behaviour of BEGIN blocks, see the perlmod man page under the heading "Package Constructors and Destructors". For an explanation of auto-vivification, see perlref, perlfaq4, perlfunc and the node Autovivification by merlyn.

    Update: Erk, weirder stuff appearing - It almost looks like something weird is happening with closures ...

     

    perl -le "print unpack'N', pack'B32', '00000000000000000000001010111111'"

      I don't understand. Why would $guid{218827} be auto-vivified? Note that $guid{218827} is referenced from a subroutine, which isn't executed until after the BEGIN block.

      I think there's a bug in Perl. Watch what happens if you dump the content of %guid in the BEGIN block:

      #!/usr/bin/perl use warnings; my %guid=(218827=>5,109940=>2); BEGIN { %handle= ( cmd => sub { my $ref = sub{$guid{218827}++;warn "sub: $guid{218827}";}; $ref->(); }, ); while (my ($key, $value) = each %guid) { print defined $key ? $key : "UNDEF"; print ":"; print defined $value ? $value : "UNDEF"; print "\n"; } } $handle{cmd}->(); __END__ sub: 6 at buu line 14.
      Not only does it show that the BEGIN block doesn't autovivify the value, the error disappears as well!

      In fact, just about any reference to %guid (like keys %guid;, %guid;, or $guid {1};) after the assignment to %handle makes the error go away.

      This ought to be perlbugged.

      Abigail

        It appears to be fixed in bleadperl. Nevertheless, a bug report with a short test case (or patch to the t/op/closure.t) would be good.
Re: modification of read-only in hash ++
by flounder99 (Friar) on Feb 05, 2004 at 16:36 UTC
    A few other notes:
    my $x=5; BEGIN {$y=sub{my $r=sub{$x++;warn "sub: $x"};$r->()}} $y->(); #sub: 1 at temp.pl line 2. #deref sub that derefs sub, my before BEGIN, using scalars => autoviv BEGIN {$y=sub{my $r=sub{$x++;warn "sub: $x"};$r->()}} my $x=5; $y->(); #sub: 1 at temp.pl line 1. #deref sub that derefs sub, BEGIN before my, using scalars => autoviv my %x=(1=>5); BEGIN {$y=sub{my $r=sub{$x{1}++;warn "sub: $x{1}"};$r->()}} $y->(); #Modification of a read-only value attempted at temp.pl line 2. #deref sub that derefs sub, my before BEGIN, using hash => ERROR BEGIN {$y=sub{my $r=sub{$x{1}++;warn "sub: $x{1}"};$r->()}} my %x=(1=>5); $y->(); #sub: 1 at temp.pl line 1. #deref sub that derefs sub, BEGIN before my, using hash => autoviv my %x=(1=>5); BEGIN {$y=sub{$x{1}++;warn "sub: $x{1}";}} $y->(); #sub: 6 at temp.pl line 2. #deref sub, my before BEGIN, using hash => uses main's my variable BEGIN {$y=sub{$x{1}++;warn "sub: $x{1}";}} my %x=(1=>5); $y->(); #sub: 1 at temp.pl line 1. #deref sub, BEGIN before my, using hash => autoviv my $x=5; BEGIN {$y=sub{$x++;warn "sub: $x"}} $y->(); #sub: 6 at temp.pl line 2. #deref sub, my before BEGIN, using scalar => uses main's my variable BEGIN {$y=sub{$x++;warn "sub: $x"}} my $x=5; $y->(); #sub: 1 at temp.pl line 1. #deref sub, BEGIN before my, using scalar => autoviv
    my head hurts! I'm getting in over my head when it comes to understanding when subs should see "my" variables. Could someone explain what results I should expect?

    I don't know how to submit a bug report so please feel free to submit one using my test cases.

    AS 5.8.2 808 / win2k

    --

    flounder

      Then this is a wonderful time to learn how to submit a bug report! It takes just a few minutes; start by reading perldoc perlbug.