http://qs321.pair.com?node_id=11120285

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

I stumbled over the fact that literal references are not readonly.

Is that a bug or am I missing an obscure reason for this???

DB<18> use Scalar::Util qw/readonly/ DB<19> p readonly 1 34283264 DB<20> p readonly undef 34283264 DB<21> p readonly {} # What? DB<22> p readonly [] # ... DB<23> p readonly bless([],"TEST") # ... DB<24> x [],{},bless([],"TEST") # all refs ARRAY(0x334ebd0) empty array HASH(0x334ec00) empty hash TEST=ARRAY(0x334eb70) empty array DB<25> x map { $_ = 1 } [],{},bless([],"TEST") # mutable! WHY??? 1 1 1 DB<26>

update

counter-examples

DB<32> x map { $_ = 1 } "str" Modification of a read-only value attempted at (eval 42)[c:/Perl_524/l +ib/perl5db.pl:737] line 2. DB<33> x map { $_ = 1 } 42 Modification of a read-only value attempted at (eval 43)[c:/Perl_524/l +ib/perl5db.pl:737] line 2. DB<34> x map { $_ = 1 } undef Modification of a read-only value attempted at (eval 44)[c:/Perl_524/l +ib/perl5db.pl:737] line 2.

update

see also Re^10: Shouldn't references be readonly? (UPDATED) for another example of the asymmetric behavior.

Cheers Rolf
(addicted to the Perl Programming Language :)
Wikisyntax for the Monastery

Replies are listed 'Best First'.
Re: Shouldn't references be readonly?
by Fletch (Bishop) on Aug 05, 2020 at 01:45 UTC

    The literal reference is just a value which in your map just happens to be contained in $_. When you reassign 1 to it, it's not changing the reference in any way, you're just putting something else in $_ (although this is where I'm getting fuzzy and agree it's somewhat weird); it's just that $_ just happens to be aliased to some temporary SV* / SVrv that the map is using as its argument list. The assignment isn't changing the referenced value, it's just sticking the new SViv 1 into that SV*.

    DB<1> $a = $b = [qw/a b c/] DB<2> x map { $_ = 1 } $a 0 1 DB<3> x $a 0 1 DB<4> x $b 0 ARRAY(0x7fa8144335b0) 0 'a' 1 'b' 2 'c'

    Hopefully someone more conversant in perlguts than I might can explain it better.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      please compare
      DB<30> x map { $_ = 1 } 2 Modification of a read-only value attempted at (eval 40)[c:/Perl_524/l +ib/perl5db.pl:737] line 2. DB<31>

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

      > The literal reference is just a value which in your map just happens to be contained in $_.

      I think you are trying to say that references are not aliased but copied in "aliasing situations" to avoid some overhead...

      But if you look at the following results you will notice that the ref of an alias is still identical while the ref of a copy isn't. (I used @R to keep the refcount up and hence avoid the reuse of freed addresses)

      DB<55> @R= map { \$_ } $a DB<56> x @R 0 REF(0x31fae58) # -> ARRAY(0x3353230) empty array DB<57> x \$a 0 REF(0x31fae58) # same -> ARRAY(0x3353230) empty array DB<58> $b=$a DB<59> x \$b 0 REF(0x3353a58) # different -> ARRAY(0x3353230) empty array DB<60>

      DB<64> p \$a == $R[0] 1 DB<65> p \$a == \$b DB<66>

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

Re: Shouldn't references be readonly?
by haj (Vicar) on Aug 05, 2020 at 09:39 UTC

    My take on this: "Literal references" don't exist in Perl, at least they don't appear in perldata. A literal reference would be something like ARRAY(0x557782f0a4c8), but you can't feed this to Perl.

    The constructs {...} and [...] are anonymous, but not literals.

    I fail to see a point in clobbering anonymous variables, but then I also fail to see why Perl should prevent it.

      [1,2,3] is called a literal array in many languages like for instance JavaScript.

      Since Perl has both an explicit $reference and a @list form of its data structures, it might be difficult to point out "the" literal form.

      > I also fail to see why Perl should prevent it.

      For the same reason why it's prevented with other readonly values like literal scalars ... to catch bugs with aliases.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        Perl doesn't have literal arrays. It has constructors which will create a reference to an anonymous array, where the elements of that array are initially assigned to by copying the values from a list which may or may not be literal. E.g.
        $ar1 = [ 1,2,3 ]; $ar2 = [ 1,2,3,$four ];
        Should @$ar1 and @$ar2 be treated differently, and if so, why?

        Dave.

        For the same reason why it's prevented with other readonly values like literal scalars ??

        As you have demonstrated, anonymous references are not readonly, so Perl does not prevent them from being overwritten. I still don't see a compelling reason why they should be read-only. Can you provide an example for "catching bugs with aliases"?

Re: Shouldn't LITERAL references be readonly? (updated)
by ikegami (Patriarch) on Aug 06, 2020 at 04:12 UTC

    Both the hash constructor ({}) and the array constructor ([]) create a fresh variable and a fresh reference to it.

    "TEST", 42 and undef do not create anything; they simply place a previously-constructed scalar on the stack. Changing these scalars would be bad, so they are protected from being changed.

    For example, take the following code:

    for (1..2) { for ((), 4..6) { say ++$_; } say ""; }

    Before 5.20, it output the following:

    5 6 7 6 7 8

    That is something that isn't read-only but should be. If "TEST", 42 or undef weren't read-only, a similar problem would occur.[1]

    There's no such problem with the newly created references from {} and []. {} and [] are akin to "<$x>" (also not read-only), not "TEST".


    1. It would be particularly bad if the scalar returned by undef wasn't read-only. That particular scalar (&PL_sv_undef) is used all over the place. For example, if ++undef didn't fail, it would cause read to start returning true on failure.
      my main point is that things like this

       map { ++$_ } [1,2,3]

      should throw an error, because the mutation of a reference itself doesn't make any sense.

      And I was asking if there is a reason why they don't.

      The whole discussion about "literals" is unfortunately only distracting,

      (though Wikipedia is on my side: Literal (computer programming): "a notation for representing a fixed value in source code" but I don't wanna continue this.

      NB: fixed doesn't mean (compile-time) constant. It means not variable)

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

        The whole discussion about "literals" is unfortunately only distracting

        uh, I didn't bring up literals at all. It's an ambiguous word which may or may not include [4,5,6] depending on who you ask. It's useless to talk about literals.

        You asked why it wasn't like "TEST", 42 and undef, and I told you why.

        If you want to ask a different question now, fine.


        map { ++$_ } [1,2,3] should throw an error

        After all the fuss you made about inconsistency when you thought it wasn't consistent, you now want to make it inconsistent?

        No, assigning a number to a scalar that previously contained a reference is odd, but it shouldn't be an error.

Re: Shouldn't references be readonly?
by jcb (Parson) on Aug 05, 2020 at 01:52 UTC

    The {} and [] operators construct new aggregates each time they are evaluated. Why should those values be readonly? They are not literals.

        Unlike a numeric literal value, [] is compiled into an opcode which calls . . . something (this is where I'm hoping someone more familiar with guts steps in) which returns a new reference value.

        $ perl -MO=Concise,-exec, -E '$a = []; $b = 5' 1 <0> enter v 2 <;> nextstate(main 2 -e:1) v:%,us,{,fea=7 3 <0> pushmark s 4 <@> anonlist sK* 5 <#> gvsv[*a] s 6 <2> sassign vKS/2 7 <;> nextstate(main 2 -e:1) v:%,us,{,fea=7 8 <$> const[IV 5] s 9 <#> gvsv[*b] s a <2> sassign vKS/2 b <@> leave[1 ref] vKP/REFC

        Opcode 4 here is doing something to allocate a new AV* and pushing the SVrv referencing that new arrayref onto the stack which then is assigned into $a. Opcode 8 is pushing the immediate literal value SViv (which is readonly) onto the stack and assigning that into $b.

        Edit: I'm mixing up the layers and perl and C and everything but . . .

        When you have map { $_ = 1 } [] in the underlying runtime what's kind of happening is that something's going:

        my @args; $args[0] = anonlist();

        then when your map EXPR, @args runs, because $_ is aliased to each item in @args something like this happens

        $args[0] = 1;

        The anonymous arrayref went into the temporary array's slot zero when it was initialized; in the map EXPR you're replacing the value in that slot in the array but you're not doing anything to the arrayref which was referenced by the previous value in that same slot. I admit that it's kind of weird that the temporary arglist slots aren't readonly but if they were made readonly then I don't think aliasing like $a = 1;for($a){ $_ = 'foo' } would work so that $a would have 'foo' afterwards (i.e. aliasing of $_ in for to a transient list of arbitrary values).

        Edit 2: while [] is a literal token it doesn't stand for a constant value.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        They're constructors for containers, not values. I'm not sure what the value would be to make [] and {} create singleton immutable containers.

        I am considering "literals" as more like constants embedded in the program, using a constant pool, where changing a value would have bizarre effects, like changing the value of "123456" everywhere in the program. Literal strings in C programs are often combined in this way.

        The {} and [] operators are defined to produce distinct mutable aggregates every time they are evaluated. In this sense, they are not literals.

        I am unsure how this behavior of replacing the reference itself is actually useful, but nor do I see any harm here.

      > The {} and [] operators construct new aggregates each time they are evaluated.

      This might come as a surprise but literals in Perl are always new as you can see by the references.°

      DB<66> p \1 SCALAR(0x335c4a0) DB<67> p \1 SCALAR(0x335c6f8) DB<68> p \1 SCALAR(0x335cae8) DB<69>

      I know there are languages where it's always the same (IMHO do Ruby and Lisp have a syntax for this) but this is not what I mean or asked.

      Alas ... few of the respondents here seem to have read the code in the OP and really have a deep grasp of aliases.

      Cheers Rolf
      (addicted to the Perl Programming Language :)
      Wikisyntax for the Monastery

      °) well undef is an exception, but it's debatable if that's a literal or a constant.

      DB<69> p \undef SCALAR(0xfb9098) DB<70> p \undef SCALAR(0xfb9098) DB<71> p \undef SCALAR(0xfb9098) DB<72> p \undef SCALAR(0xfb9098) DB<73> p \"str" SCALAR(0x335d3c0) DB<74> p \"str" SCALAR(0x335d498) DB<75> p \"str" SCALAR(0x335d270)

        That seems to be because a reference to a literal actually constructs a new scalar; note that the examples you gave where an error was thrown at an attempt to modify a read-only value did not involve references, but only aliases to literal values. I suspect that the anonymous scalar you get when you say \1 is probably modifiable. There is no such thing as a literal aggregate in Perl — the {} and [] operators are defined as constructors instead.