Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling

Calling splice() on Immutable Arrays

by SankoR (Prior)
on Jul 13, 2016 at 01:59 UTC ( #1167665=perlquestion: print w/replies, xml ) Need Help??

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

I've got an edge case no one in their right mind would stumble upon. This time an oldie from the days when Readonly::XS was needed but still matters today for Readonly (edit: which I currently maintain), Const::Fast (edit: which is the result of Readonly's inherently broken modus operandi), etc.; modules that wrap perl's own internal means of marking variables as read-only. Consider this:
my @a = qw[a simple list]; Internals::SvREADONLY(@a, 1); Internals::SvREADONLY($a[1], 1); # Make our target element RO (dou +ble sure!) warn '$a[1] is ' . (Internals::SvREADONLY($a[1]) ? '' : 'not ') . +'read-only'; warn join ' ', @a; eval { $a[1] = 'changed' }; # Modification of a read-only val +ue attempted warn join ' ', @a; splice(@a, 1, 1, 'spliced'); warn join ' ', @a; warn '$a[1] is ' . (Internals::SvREADONLY($a[1]) ? '' : 'not ') . +'read-only';
...the output of the above reads...
    $a[1] is read-only at line 3.
    a simple list at line 4.
    a simple list at line 6.
    a spliced list at line 8.
    $a[1] is not read-only at line 9.

splice(...) can replace read-only elements where setting them with an operator throws an exception because the actual element is replaced internally rather than just the value. ...even though the container is immutable. splice(...) can also delete elements from a immutable array where delete() will fail:

my @b = qw[another simple list]; Internals::SvREADONLY(@b, 1);# Matters here warn join ' ', @b; # "another simple list" eval { delete $b[1] }; # throws Modification of... warn join ' ', @b; # "another simple list" splice(@b, 1, 1); # Zot! warn join ' ', @b; # "another list"
It's been a long day but to me, this is unexpected behavior. It's certainly low priority (due to it being a feature of Internals::) but before I report it as a bug on RT, I'm seeking opinions.

Code tested on Strawberry Perl v5.20.1 (MSWin32-x64-multi-thread) but this originally came to me years ago.

Replies are listed 'Best First'.
Re: Calling splice() on Immutable Arrays
by choroba (Archbishop) on Jul 13, 2016 at 09:34 UTC
    How does it matter for Readonly? You can't splice a Readonly array.
    #!/usr/bin/perl use warnings; use strict; use feature qw{ say }; use Readonly; Readonly my @arr => qw( a b c ); say "@arr"; eval { $arr[1] = 'B' }; say "@arr"; eval { splice @arr, 1, 1, 'C' }; say "@arr"; __END__ Output: a b c a b c a b c
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
      Currently, unlike scalars, Readonly's arrays and hashes are still based on tie(). This splice conundrum is one of the last things keeping me from moving totally away from tie magic (which, besides speed, has its own problems some of which are sitting on RT) on modern perl. I'd hate to get rid of one set of issues only to introduce a totally new set.

      Edit: I'm the maintainer of Readonly, btw. And by 'modern' I mean v5.8.8 or higher.

Re: Calling splice() on Immutable Arrays
by shmem (Chancellor) on Jul 13, 2016 at 20:02 UTC
    even though the container is immutable

    It isn't. It looks like splice doesn't care about SvREADONLY at all (but unshift does). You can extend such a list an array via splice:

    @a = qw[a simple list]; warn join ' ', @a; # "a simple list" Internals::SvREADONLY(@a, 1); # Make the list RO (doesn't matter he +re, actually) Internals::SvREADONLY($a[1], 1); # Make our target element RO (double +sure!) warn '@a is ' . (Internals::SvREADONLY(@a) ? '' : 'not ') . 'read-only +'; splice(@a, 1, 1, qw(not quite readonly)); warn join ' ', @a; # "a not quite readonly list" warn '@a is ' . (Internals::SvREADONLY(@a) ? '' : 'not ') . 'read-only +'; unshift @a, qw*this is*; __END__ a simple list at line 2. @a is read-only at line 5. a not quite readonly list at line 7. @a is read-only at line 8. Modification of a read-only value attempted at line 9 +.

    This is a bug (incomplete implementation, maybe also a design issue). It looks like Internals::SvREADONLY has been tacked onto, not "bolted through" the perl source

    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      Yes, you're correct. It's supposed to be immutable but isn't. I only picked it up again yesterday but all the array related functions I've tried pass (by failing) except splice. I'll take this to RT in the morning and let them sort it out. :)

      Thanks! Good to have a second or third pair of eyes.

        A quick look at the source (pp_splice() in pp.c), shows that it doesn't check the state of the readonly flag. a simple 1 or two line patch should fix it.

        That said, I cannot remember the last time I accidentally modified the contents of an array I didn't want modified, so it's a moot point as far as I'm concerned.

        Indeed, if some module or api gave me an array that was readonly and I wanted to modify it, I'd just turn the flag off and do it anyway.

        This certainly isn't anything that would stop me from replacing the use of Readonly if I encountered its use in some module I wanted to use.

        But different folks ...

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". I knew I was on the right track :)
        In the absence of evidence, opinion is indistinguishable from prejudice.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1167665]
Front-paged by BrowserUk
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (7)
As of 2020-12-04 12:11 GMT
Find Nodes?
    Voting Booth?
    How often do you use taint mode?

    Results (58 votes). Check out past polls.