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

I'm experimenting, idly, with lvalues, but instead it looks like some dreadful, purely syntactic, confusion:

use strict; use warnings; use feature 'say'; my $p = 'abc'; my $q = 'abc'; substr(( substr( $p, 0, 1 ) = '123' ), 0, 1 ) = 'x'; substr((( substr $q, 0, 1 ) = '123' ), 0, 1 ) = 'x'; say $p; say $q;

x23bc 123bc

Replies are listed 'Best First'.
Re: Why Perl gets confused here?
by NetWallah (Canon) on Oct 26, 2017 at 05:57 UTC
    You have som extra parens.
    Here is how the code is being interpreted:
    substr(substr($p, 0, 1) = '123', 0, 1) = 'x'; substr((substr($q, 0, 1)) = '123', 0, 1) = 'x';
    B::Deparse is your friend.

                    All power corrupts, but we need electricity.

      i dont think extra parens kill lvalues
      $ perl -le 'sub x:lvalue{$a} x="y";print$a' y $ perl -le 'sub x:lvalue{$a} (x)="y";print$a' y
        $ perl -le'sub x:lvalue{$a} print scalar( x="y" );' y $ perl -le'sub x:lvalue{$a} print scalar( (x)="y" );' 1
Re: Why Perl gets confused here?
by ikegami (Patriarch) on Oct 26, 2017 at 22:07 UTC

    You made two changes. You removed the parens around the arguments, and you added parens around substr. The former is inconsequential here, so let's start by reverting it to make things clearer. This means you have

    ... substr( $p, 0, 1 ) = '123' ... ... ( substr( $q, 0, 1 ) ) = '123' ...

    There are two different assignment operators: the scalar assignment operator and the list assignment operator.

    To which operator = compiles is determined by the left-hand side (LHS) of the operator. If the LHS look "list-ish", a list assignment operator will be used. Otherwise, a scalar assignment operator will be used.

    In the first case, the LHS of the assignment is subtr(...). That's not "list-ish", so a scalar assignment operator is used. A scalar assignment operator in scalar context[1] returns its LHS, so this assignment evaluates the result of substr.

    In the second case, the LHS of the assignment is ( ... ). That is "list-ish", so a list assignment operator is used. A list assignment operator in scalar context[1] returns the number of scalars to which its RHS evaluates, so this assignment evaluates to (a fresh scalar with value) 1.

    The following is a variant of your code that allows us to check the values returned by the assignments.

    use strict; use warnings; use feature 'say'; my $p = 'abc'; my $q = 'abc'; my $ref_p = \scalar( substr( $p, 0, 1 ) = '123' ); my $ref_q = \scalar( ( substr( $q, 0, 1 ) ) = '123' ); say $$ref_p; # 123 say $$ref_q; # 1 substr( $$ref_p, 0, 1 ) = 'x'; substr( $$ref_q, 0, 1 ) = 'x'; say $$ref_p; # x23 say $$ref_q; # x say $p; # x23bc say $q; # 123bc

    Reference: Mini-Tutorial: Scalar vs List Assignment Operator

    1. substr evaluates its first operand in scalar context.

      Thank you for explanation.

      use strict; use warnings; use feature 'say'; use Devel::Peek; my $p = 'abc'; my $q = 'abc'; Dump(( substr $p, 0, 1 ) = '123' ); Dump( time ); (( substr( $q, 0, 1 )) = '123' ) = 'x'; say $q; #substr(( time ), 0, 1 ) = 'x';

      SV = IV(0xa72034) at 0xa72034 REFCNT = 1 FLAGS = (PADTMP,IOK,pIOK) IV = 1 SV = IV(0xa8bb44) at 0xa8bb44 REFCNT = 1 FLAGS = (PADTMP,IOK,pIOK) IV = 1509111305 xbc

      Shouldn't the return value of list assignment operator be treated as constant, then? I tried to "sneak" various types of constants or constant-producing expressions as arg to lvalue-substr, but Perl reports compile time error (commented line).

      More confusing is "xbc" -- I'd expect $q to be "123bc", and "x" to be gone into fathomless void, as in OP?

        Context matters.

        This is list context  (( substr( $q, 0, 1 )) = '123' ) = 'x';

        But the OP happened in scalar context, see Ikegami's footnote.

        "substr evaluates its first operand in scalar context."

        > More confusing is "xbc" -- I'd expect $q to be "123bc", and "x" to be gone into fathomless void, as in OP?

        Depending on your Perl version you can assign multiple times to the same lvalue.

        in this case first 123 then x

        See substr documentation

        Note that the lvalue returned by the three-argument version of substr acts as a 'magic bullet'; each time it is assigned to, it remembers which part of the original string is being modified

        To avoid confusion, I'd suggest using the 4 parametric version instead of the lvalue variant.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!

Re: Why Perl gets confused here?
by LanX (Saint) on Oct 26, 2017 at 22:16 UTC
    If you look into the optree you'll see a list assignment aassign * happening in the second example instead of an scalar assignment sassign
    C:\Windows\system32>perl -MO=Concise -E"substr(( substr( $p, 0, 1 ) = + '123' ), 0, 1 ) = 'x';" d <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 47 -e:1) v:%,{,469764096 ->3 - <1> ex-sassign vKS/2 ->d c <@> substr[t5] vKS/REPL1ST,3 ->- - <0> ex-pushmark s ->4 3 <$> const[PV "x"] s ->4 9 <2> sassign sKPRMS*/2 ->a 4 <$> const[PV "123"] s ->5 8 <@> substr[t3] sKRM*/3 ->9 - <0> ex-pushmark s ->5 - <1> ex-rv2sv sKRM*/1 ->6 5 <#> gvsv[*p] s ->6 6 <$> const[IV 0] s ->7 7 <$> const[IV 1] s ->8 a <$> const[IV 0] s ->b b <$> const[IV 1] s ->c -e syntax OK C:\Windows\system32>perl -MO=Concise -E"substr((( substr $q, 0, 1 ) = + '123' ), 0, 1 ) = 'x'" f <@> leave[1 ref] vKP/REFC ->(end) 1 <0> enter ->2 2 <;> nextstate(main 47 -e:1) v:%,{,469764096 ->3 - <1> ex-sassign vKS/2 ->f e <@> substr[t6] vKS/REPL1ST,3 ->- - <0> ex-pushmark s ->4 3 <$> const[PV "x"] s ->4 b <2> aassign[t4] sKPRMS* ->c - <1> ex-list lK ->6 4 <0> pushmark s ->5 5 <$> const[PV "123"] s ->6 - <1> ex-list lK ->b 6 <0> pushmark s ->7 a <@> substr[t3] sKPRM*/3 ->b - <0> ex-pushmark s ->7 - <1> ex-rv2sv sKRM*/1 ->8 7 <#> gvsv[*q] s ->8 8 <$> const[IV 0] s ->9 9 <$> const[IV 1] s ->a c <$> const[IV 0] s ->d d <$> const[IV 1] s ->e -e syntax OK

    edit

    see ikegami's answer here Re: Why Perl gets confused here?

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

    *) aassign for "array" assignment is one of these misnomers like wantarray , should rather be named lassign

Re: Why Perl gets confused here?
by LanX (Saint) on Oct 26, 2017 at 21:32 UTC
    The evaluation order of sub arguments with side effects are a tricky thing in Perl.

    See  eval order of args to a sub

    I think passing an lvalue is very similar to passing two increments and you enter the undiscovered country of implementation details which might change.

    update

    nope, see answers pointing to "list assignment"

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!