Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Multiple assignments in assignment ops #16488

Open
p5pRT opened this issue Apr 5, 2018 · 8 comments
Open

Document Multiple assignments in assignment ops #16488

p5pRT opened this issue Apr 5, 2018 · 8 comments

Comments

@p5pRT
Copy link

@p5pRT p5pRT commented Apr 5, 2018

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

Searchable as RT133064$

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Apr 5, 2018

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
Copy link
Author

@p5pRT p5pRT commented Apr 6, 2018

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
Copy link
Author

@p5pRT p5pRT commented Apr 6, 2018

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

@p5pRT
Copy link
Author

@p5pRT p5pRT commented Apr 10, 2018

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
Copy link
Author

@p5pRT p5pRT commented Apr 11, 2018

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
Copy link
Author

@p5pRT p5pRT commented Apr 11, 2018

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@​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
Copy link
Author

@p5pRT p5pRT commented Apr 11, 2018

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@​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
Copy link
Author

@p5pRT p5pRT commented Apr 11, 2018

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@​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.

@xenu xenu removed the Severity Low label Dec 29, 2021
@khwilliamson khwilliamson changed the title Multiple assignments in assignment ops Document Multiple assignments in assignment ops Apr 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants