views:

90

answers:

4

Below is my code (don't worry there's a USUW at the top of the module)

I'm testing if an array ref is readonly, and if that is the case then I'm copying it into another array ref. The tests show that the array is not read-only, yet when it runs, it fails with that error. ( For those of you not familiar with me or Smart::Comments--those ### are Smart::Comments.)

### readonly( $arg_ref ) : readonly( $arg_ref )
### readonly( @$arg_ref ) : readonly( @$arg_ref )
my @ro = map { readonly( $_ ) } @$arg_ref;
### @ro
if ( readonly $arg_ref ) {
    $arg_ref = [ @$arg_ref ];
}
return map { my $val = shift @$arg_ref;
             $_ => $val 
            } @_ 
            ;

This is the output I get:

### readonly( $arg_ref ) : 0
### readonly( @$arg_ref ) : 0

### @ro: [
###        0,
###        0,
###        0,
###        0,
###        0
###      ]

But here's the error:

Modification of a read-only value attempted at ....pm line 247.

(247 is:

return map { my $val = shift @$arg_ref;

)

Does anybody have any familiarity with this issue? We're running Perl 5.8.7. Any idea on how to address it?

+1  A: 

If the reference from DBI::fetchrow_arrayref is coming back read-only, attempting to overwrite it won't help: the reference is read-only, not the thingie (i.e., the array with column values).

Make your own copy right at the source if you need destructive updates, e.g.,

my $arg_ref = [ $sth->fetchrow_array ];
Greg Bacon
@Axeman But if `$arg_ref` may be read-only, destructive operations such as `shift` aren't suitable, which you've observed.
Greg Bacon
@gbacon: indeed and part of programming is solving problems. A queue is *transportable* a "list stream" isn't unless you want to cobble together a combination of list and current spot in the list. But that would never do more than a good old fashioned queue that you get for free with a Perl array. Now, I could *always* copy it, but a "performance and efficiency weenie" like myself gets hives thinking about not doing only what is needed.
Axeman
@Axeman Answer updated—destructively, even!
Greg Bacon
Axeman
A: 

It doesn't look like the result of Scalar::Util::readonly can be trusted used how you want to use it. Witness:

perl -MScalar::Util=readonly -MReadonly -wle'
    Readonly my $arg_ref => [ qw(a b c)];
    print readonly $arg_ref;
    $arg_ref = 1;'

prints:

0
 Modification of a read-only value attempted at -e line 1.

(Tested under perl5.8.8 with Readonly 1.03, Scalar::Util 1.23)

Ether
That's a shame--and in a core module, too...
Axeman
I just ran the test above on perl5.12.1 and got the same result. :(
Ether
That's because Readonly and readonly are different things. :) See my answer for why.
Robert P
@Robert: good to know. This has probably confused more than a few people.
Ether
@Axeman the problem lies not in the core module...
Leon Timmermans
I don't like the "solution", but watching out for readonly's failures seems to be it. I coded a redo loop to address it--something I hadn't done since coding in *VB*!!
Axeman
+1  A: 

Readonly and readonly are different. (Notice the capital R and lowercase r. ;-) )

See the Scalar::Util documentation for why this is true:

readonly SCALAR

Returns true if SCALAR is readonly.

sub foo { readonly($_[0]) }
$readonly = foo($bar);              # false
$readonly = foo(0);                 # true

This is more about aliases (such as those passed in via foreach loops or in @_, and not the Readonly module.

Robert P
So is there any way of checking if a variable was declared Readonly with a capital R?
Ether
Both Readonly::XS and the XS implementation of readonly rely on the same flag (SvREADONLY), so they should be the same (if you're using the XS versions, which you should). I smell a bug here.
Leon Timmermans
@Ether, IIUC, `ref(tied( A_VARIABLE )) =~ /^Readonly::/` should do the trick, and you'll want to fall back to checking `Scalar::Util::readonly` if that check is false.
pilcrow
@Leon, can you expand on that? Readonly.pm 1.03 sure looks to me like it checks `$XSokay` (i.e., is `Readonly::XS` present?), setting SvREADONLY if so *but otherwise* doing a `tie` to a `Readonly::Foo` class that has no working mutators in the pure perl logic branches...
pilcrow
I've just posted an answer detailing what's going on. It's a Readonly bug.
Leon Timmermans
+1  A: 

I think I have it figured out. Robert P was right, but he only got half of the answer: Readonly and readonly do different things though they should do the same thing, the fact that they don't is a bug. I'll try to explain what is going on.

Readonly has two implementations for scalars: one (Readonly::XS) that is based on the SvREADONLY flag and one (Readonly::Scalar) based on ties that emulates the SvREADONLY flag.

readonly also has two implementations, one in Perl (which does self assignment and checks if it throws an exception or not), and one in XS (again based on the SvREADONLY flag).

There are 4 possible combinations, and unfortunately the one that is most common (pure perl Readonly and XS readonly) is the only one that doesn't work as advertised.

Here's the crux: sub Readonly::Readonly actually doesn't use Readonly::XS at all, only sub Readonly::Scalar does. This is probably a bug. readonly correctly reports that the variable is not a readonly variable: its readonlyness is faked by a tie.

It's Readonly that's at fault here IMO, it should do the right thing when it can and document when it can't. Right now it's doing neither of those.

Leon Timmermans
From my perspective, there's no issue with `Readonly`, we don't even have it installed in our environment. The only thing this concerns is `Scalar::Util::readonly`'s inability to tell me that a array reference an array reference is readonly until I try to shift on it.
Axeman
Actually it seems that its not the array *reference* isn't readonly but the array it refers to. A subtle but important difference.
Leon Timmermans
This behaviour definitely contradicts the documentation -- it says that when one has Readonly::XS installed, it is used in all cases for scalars (but not arrays and hashes). Bummer. (So who's going to file a bug, or even better submit a patch?) :)
Ether
I've already written a patch, be the author hasn't replied to my email yet.
Leon Timmermans