Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.94k stars 554 forks source link

Document Multiple assignments in assignment ops #16488

Open p5pRT opened 6 years ago

p5pRT commented 6 years ago

Migrated from rt.perl.org#133064 (status was 'open')

Searchable as RT133064$

p5pRT commented 6 years ago

From dp13@sanger.ac.uk

The following are not equivalent\, which I found surprising​:

$ perl -wle 'my $foo = {}; $foo->{b} = defined $foo->{b} ? $foo->{b} : scalar keys %$foo; print $foo->{b}' 0

$ perl -wle 'my $foo = {}; $foo->{b} //= scalar keys %$foo; print $foo->{b}' 1

Assignment operators other than '=' all appear to\, in effect\, assign the lvalue to undef first (if it does not exist); only then is the new value calculated... a bit like autovivification. This can be confirmed by jumping out of the operation before it completes​:

$ perl -wle 'my $foo = {}; for (1) { $foo->{b} += 1 + next; } print for keys %$foo' b

This seems like one of those apparent 'bugs' for which there is a good and interesting reason... but I would have expected to find this behaviour made clear somewhere in the documentation on perlop or\, failing that\, perlref\, and I cannot see it. Is it intentional?

I tested on v5.18.2 v5.22.4 which I happened to have lying around at the time and did not find anything relevant in the major perldeltas since then.

Daniel

-- The Wellcome Sanger Institute is operated by Genome Research Limited\, a charity registered in England with number 1021457 and a company registered in England with number 2742969\, whose registered office is 215 Euston Road\, London\, NW1 2BE.

p5pRT commented 6 years ago

From @cpansprout

On Thu\, 05 Apr 2018 09​:48​:30 -0700\, dp13@​sanger.ac.uk wrote​:

The following are not equivalent\, which I found surprising​:

$ perl -wle 'my $foo = {}; $foo->{b} = defined $foo->{b} ? $foo->{b} : scalar keys %$foo; print $foo->{b}' 0

$ perl -wle 'my $foo = {}; $foo->{b} //= scalar keys %$foo; print $foo->{b}' 1

Assignment operators other than '=' all appear to\, in effect\, assign the lvalue to undef first (if it does not exist); only then is the new value calculated... a bit like autovivification. This can be confirmed by jumping out of the operation before it completes​:

$ perl -wle 'my $foo = {}; for (1) { $foo->{b} += 1 + next; } print for keys %$foo' b

This seems like one of those apparent 'bugs' for which there is a good and interesting reason... but I would have expected to find this behaviour made clear somewhere in the documentation on perlop or\, failing that\, perlref\, and I cannot see it. Is it intentional?

I don’t know offhand whether this is documented\, but the difference is explained by the fact that regular assignment (=) evaluates the right-hand side first. (It has to work this way for common idioms like ‘local $x = $x’ to work.) Assignment versions of other operators\, however\, evaluate the lefthand side first. (In the case of //= &&= etc.\, it has to be this way\, as the lhs determines whether the rhs is evaluated.)

--

Father Chrysostomos

p5pRT commented 6 years ago

The RT System itself - Status changed from 'new' to 'open'

p5pRT commented 6 years ago

From @davidnicol

It's clearly an unwanted autovivification bug\, but can we call it an optimization? That is\, an or-assign autovivs because it knows its going to need the l-value in a jiffy or two?

RHS evaluated first​: $ perl -le '$foo{x} = scalar keys %foo; print $foo{x}' 0

Container slot evaluated first\, but as as r-value\, does not autoviv $ perl -le '$foo{x} or $foo{x} = scalar keys %foo; print $foo{x}' 0

assignment op accesses container slot only once\, as l-value\, which autovivs $ perl -le '$foo{x} ||= scalar keys %foo; print $foo{x}' 1

even when it turns out the assignment isn't going to happen. $ perl -le '$foo{x} &&= scalar keys %foo; print keys %foo' x

The documentation   Assignment operators work as in C. That is\, $a += 2;   is equivalent to $a = $a + 2; although without duplicating any side effects that dereferencing the lvalue might trigger\, such as from tie(). Other assignment operators work similarly.

does imply that the LHS of an assignment op is going to get dereferenced as an lvalue\, not an r-value and then as an lvalue\, with the "although without duplicating" language there.

which gives us the "good and interesting reason" of "Assignment operators treat their left hand sides as l-values\, which means autovivifying slots in containers."

It should be possible to adapt the crazy gymnastics Perl goes through to avoid autovivifying non-existent container slots when they are used in subroutine calls until their magical proxy l-value placeholders are actually assigned-to to the conditional assignment operators\, but is it worth it and would it break things?

On Thu\, Apr 5\, 2018 at 7​:36 PM\, Father Chrysostomos via RT \< perlbug-followup@​perl.org> wrote​:

On Thu\, 05 Apr 2018 09​:48​:30 -0700\, dp13@​sanger.ac.uk wrote​:

The following are not equivalent\, which I found surprising​:

$ perl -wle 'my $foo = {}; $foo->{b} = defined $foo->{b} ? $foo->{b} : scalar keys %$foo; print $foo->{b}' 0

$ perl -wle 'my $foo = {}; $foo->{b} //= scalar keys %$foo; print $foo->{b}' 1

Assignment operators other than '=' all appear to\, in effect\, assign the lvalue to undef first (if it does not exist); only then is the new value calculated... a bit like autovivification. This can be confirmed by jumping out of the operation before it completes​:

$ perl -wle 'my $foo = {}; for (1) { $foo->{b} += 1 + next; } print for keys %$foo' b

This seems like one of those apparent 'bugs' for which there is a good and interesting reason... but I would have expected to find this behaviour made clear somewhere in the documentation on perlop or\, failing that\, perlref\, and I cannot see it. Is it intentional?

I don’t know offhand whether this is documented\, but the difference is explained by the fact that regular assignment (=) evaluates the right-hand side first. (It has to work this way for common idioms like ‘local $x = $x’ to work.) Assignment versions of other operators\, however\, evaluate the lefthand side first. (In the case of //= &&= etc.\, it has to be this way\, as the lhs determines whether the rhs is evaluated.)

--

Father Chrysostomos

--- via perlbug​: queue​: perl5 status​: new https://rt-archive.perl.org/perl5/Ticket/Display.html?id=133064

-- “no man should be compelled to do what the laws do not require; nor to refrain from acts which the laws permit.” Calder v. Bull (U.S. 1798)

p5pRT commented 6 years ago

From @iabyn

On Tue\, Apr 10\, 2018 at 10​:49​:26AM -0500\, David Nicol wrote​:

It should be possible to adapt the crazy gymnastics Perl goes through to avoid autovivifying non-existent container slots when they are used in subroutine calls until their magical proxy l-value placeholders are actually assigned-to to the conditional assignment operators\, but is it worth it and would it break things?

No\, it's not worth it.

-- All wight. I will give you one more chance. This time\, I want to hear no Wubens. No Weginalds. No Wudolf the wed-nosed weindeers.   -- Life of Brian

p5pRT commented 6 years ago

From @davidnicol

and if you really need it you can do it in Perl. "navdora" means non-autovivifying defined-or-assign. This was done with 5.22\, which is after passing hash elements as subroutine arguments stopped autovivving them​:

$ perl -le 'sub navdora($&)​:lvalue{defined$_[0]?$_[0]​:$_[0]=&{$_[1]}};navdora $foo{x}\,sub{scalar keys %foo}; print keys %foo\,values%foo' x0

$ perl -le 'sub navdora($&)​:lvalue{defined$_[0]?$_[0]​:$_[0]=&{$_[1]}};navdora($foo{x}\,sub{scalar keys %foo})=9; print keys %foo\,values%foo' x9

in practice it would surely make more sense to simply prefer a ternary over a //= rather than living with that ugly calling convention.

On Wed\, Apr 11\, 2018 at 2​:34 AM\, Dave Mitchell \davem@&#8203;iabyn\.com wrote​:

On Tue\, Apr 10\, 2018 at 10​:49​:26AM -0500\, David Nicol wrote​:

It should be possible to adapt the crazy gymnastics Perl goes through to avoid autovivifying non-existent container slots when they are used in subroutine calls until their magical proxy l-value placeholders are actually assigned-to to the conditional assignment operators\, but is it worth it and would it break things?

No\, it's not worth it.

-- All wight. I will give you one more chance. This time\, I want to hear no Wubens. No Weginalds. No Wudolf the wed-nosed weindeers. -- Life of Brian

-- “no man should be compelled to do what the laws do not require; nor to refrain from acts which the laws permit.” Calder v. Bull (U.S. 1798)

p5pRT commented 6 years ago

From dp13@sanger.ac.uk

FYI re //=

Currently it sounds like if you squint at the docs hard enough it is implied. It might be a bug but not worth fixing.

For context\, Dave Mitchell does a lot of the hard work that someone needs to do deep in the bowels of perl.

Daniel

-----Original Message----- From​: David Nicol via RT [mailto​:perlbug-followup@​perl.org] Sent​: 11 April 2018 14​:34 To​: Daniel Perrett Subject​: Re​: [perl #133064] Multiple assignments in assignment ops

and if you really need it you can do it in Perl. "navdora" means non-autovivifying defined-or-assign. This was done with 5.22\, which is after passing hash elements as subroutine arguments stopped autovivving them​:

$ perl -le 'sub navdora($&)​:lvalue{defined$_[0]?$_[0]​:$_[0]=&{$_[1]}};navdora $foo{x}\,sub{scalar keys %foo}; print keys %foo\,values%foo' x0

$ perl -le 'sub navdora($&)​:lvalue{defined$_[0]?$_[0]​:$_[0]=&{$_[1]}};navdora($foo{x}\,sub{scalar keys %foo})=9; print keys %foo\,values%foo' x9

in practice it would surely make more sense to simply prefer a ternary over a //= rather than living with that ugly calling convention.

On Wed\, Apr 11\, 2018 at 2​:34 AM\, Dave Mitchell \davem@&#8203;iabyn\.com wrote​:

On Tue\, Apr 10\, 2018 at 10​:49​:26AM -0500\, David Nicol wrote​:

It should be possible to adapt the crazy gymnastics Perl goes through to avoid autovivifying non-existent container slots when they are used in subroutine calls until their magical proxy l-value placeholders are actually assigned-to to the conditional assignment operators\, but is it worth it and would it break things?

No\, it's not worth it.

-- All wight. I will give you one more chance. This time\, I want to hear no Wubens. No Weginalds. No Wudolf the wed-nosed weindeers. -- Life of Brian

-- “no man should be compelled to do what the laws do not require; nor to refrain from acts which the laws permit.” Calder v. Bull (U.S. 1798)

-- The Wellcome Sanger Institute is operated by Genome Research Limited\, a charity registered in England with number 1021457 and a company registered in England with number 2742969\, whose registered office is 215 Euston Road\, London\, NW1 2BE.

p5pRT commented 6 years ago

From dp13@sanger.ac.uk

Sorry\, that was meant to be a forward to someone else and I pressed reply instead.

Thanks all for looking at this. I am content for this bug to be closed\, although the docs could definitely be clearer. I am willing to try writing something.

Daniel

-----Original Message----- From​: Daniel Perrett Sent​: 11 April 2018 14​:41 To​: 'perlbug-followup@​perl.org' Subject​: RE​: [perl #133064] Multiple assignments in assignment ops

FYI re //=

Currently it sounds like if you squint at the docs hard enough it is implied. It might be a bug but not worth fixing.

For context\, Dave Mitchell does a lot of the hard work that someone needs to do deep in the bowels of perl.

Daniel

-----Original Message----- From​: David Nicol via RT [mailto​:perlbug-followup@​perl.org] Sent​: 11 April 2018 14​:34 To​: Daniel Perrett Subject​: Re​: [perl #133064] Multiple assignments in assignment ops

and if you really need it you can do it in Perl. "navdora" means non-autovivifying defined-or-assign. This was done with 5.22\, which is after passing hash elements as subroutine arguments stopped autovivving them​:

$ perl -le 'sub navdora($&)​:lvalue{defined$_[0]?$_[0]​:$_[0]=&{$_[1]}};navdora $foo{x}\,sub{scalar keys %foo}; print keys %foo\,values%foo' x0

$ perl -le 'sub navdora($&)​:lvalue{defined$_[0]?$_[0]​:$_[0]=&{$_[1]}};navdora($foo{x}\,sub{scalar keys %foo})=9; print keys %foo\,values%foo' x9

in practice it would surely make more sense to simply prefer a ternary over a //= rather than living with that ugly calling convention.

On Wed\, Apr 11\, 2018 at 2​:34 AM\, Dave Mitchell \davem@&#8203;iabyn\.com wrote​:

On Tue\, Apr 10\, 2018 at 10​:49​:26AM -0500\, David Nicol wrote​:

It should be possible to adapt the crazy gymnastics Perl goes through to avoid autovivifying non-existent container slots when they are used in subroutine calls until their magical proxy l-value placeholders are actually assigned-to to the conditional assignment operators\, but is it worth it and would it break things?

No\, it's not worth it.

-- All wight. I will give you one more chance. This time\, I want to hear no Wubens. No Weginalds. No Wudolf the wed-nosed weindeers. -- Life of Brian

-- “no man should be compelled to do what the laws do not require; nor to refrain from acts which the laws permit.” Calder v. Bull (U.S. 1798)

-- The Wellcome Sanger Institute is operated by Genome Research Limited\, a charity registered in England with number 1021457 and a company registered in England with number 2742969\, whose registered office is 215 Euston Road\, London\, NW1 2BE.