develooper Front page | perl.perl5.porters | Postings from July 2019

Re: [perl #133809] Perl silently adds a key to hash

Thread Previous | Thread Next
From:
demerphq
Date:
July 14, 2019 14:29
Subject:
Re: [perl #133809] Perl silently adds a key to hash
Message ID:
CANgJU+XE9Vbi73bUmO3L6pmEWE0jWZ3C6_Kg0beJSegh65pYPA@mail.gmail.com
On Sun, 14 Jul 2019, 06:37 Deven T. Corzine, <deven@ties.org> wrote:

> On Sat, Jul 13, 2019 at 4:52 PM Karen Etheridge <perl@froods.org> wrote:
>
>> On Sat, Jul 13, 2019 at 10:43 AM Deven T. Corzine <deven@ties.org> wrote:
>>
>> > I would love to have the other discussion about whether the current
>> behavior could be changed (perhaps with a pragma) to avoid autovivification
>>
>> We have one.
>>
>>     no autovivification;
>>
>> See https://metacpan.org/pod/autovivification.
>>
>
> Thanks for pointing that out!  I was aware of that module, but some people
> who weren't aware of it might find it useful.  While this module is good
> for calling attention to unexpected autovivification (only when it actually
> occurs), it doesn't quite provide the behavior I was suggesting.
>
> For example, here is the default behavior, which autovivifies two new
> hashes nested under $x due to a simple read-only access in non-existent
> sub-hashes:
>
> $ perl -MData::Dx -e 'my $x = { foo => { bar => {} } }; Dx $x; $y =
>> $x->{foo}{bar}{testing}{autovivification}{pragma}; Dx $y; Dx $x;'
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>> #line 1  -e
>> $y = undef
>> #line 1  -e
>> $x = { foo => { bar => { testing => { autovivification => {} } } } }
>
>
> At first glance, "no autovivification" appears to fix this:
>
> $ perl -MData::Dx -e 'no autovivification; my $x = { foo => { bar => {} }
>> }; Dx $x; my $y = $x->{foo}{bar}{testing}{autovivification}{pragma}; Dx $y;
>> Dx $x;'
>
> #line 1  -e
>> $x = { foo => { bar => {} } }
>> #line 1  -e
>> $y = undef
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>
>
> However, if you use that non-existent reference as a subroutine parameter,
> the same problem comes back:
>

It *must* do so.

sub Foo { $_[0]= "Foo" }
my %hash;
Foo($hash{Foo});
is($hash{Foo},"Foo")

Is expected to pass. Arguments to subs are passed as lvalues, and lvalue
context alway autovivfies.

This is also why this behaviour cannot be changed without opt in from the
user, far too many cases would completely break existing programs.


> $ perl -MData::Dx -e 'no autovivification; sub foo { return $_[0]; }; my
>> $x = { foo => { bar => {} } }; Dx $x; my $y =
>> foo($x->{foo}{bar}{testing}{autovivification}{pragma}); Dx $y; Dx $x;'
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>> #line 1  -e
>> $y = undef
>> #line 1  -e
>> $x = { foo => { bar => { testing => { autovivification => {} } } } }
>
>
> My guess is that this relates to subroutine parameters being passed by
> reference.  Using a hard reference actually autovivifies even deeper:
>

They are passed as aliases, slightly different concept than a reference.
(An alias is essentially a reference which does not need to be
dereferenced).


> $ perl -MData::Dx -e 'no autovivification; my $x = { foo => { bar => {} }
>> }; Dx $x; my $y = \$x->{foo}{bar}{testing}{autovivification}{pragma}; Dx
>> $y; Dx $x;'
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>> #line 1  -e
>> $y = \undef
>> #line 1  -e
>> $x = {
>>        foo => {
>>                 bar => { testing => { autovivification => { pragma =>
>> undef } } },
>>               },
>>      }
>
>
> The default options for "no autovivification" are "fetch exists delete".
> Adding "store" to this list does catch the autovivification attempt, but
> defaults to a fatal error:
>
> $ perl -MData::Dx -e 'no autovivification qw[fetch exists delete store];
>> sub foo { return $_[0]; }; my $x = { foo => { bar => {} } }; Dx $x; my $y =
>> foo($x->{foo}{bar}{testing}{autovivification}{pragma}); Dx $y; Dx $x;'
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>> Can't vivify reference at -e line 1.
>
>
> You can add "warn" to the options to make this non-fatal:
>
> $ perl -MData::Dx -e 'no autovivification qw[fetch exists delete store
>> warn]; sub foo { return $_[0]; }; my $x = { foo => { bar => {} } }; Dx $x;
>> my $y = foo($x->{foo}{bar}{testing}{autovivification}{pragma}); Dx $y; Dx
>> $x;'
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>> Reference was vivified at -e line 1.
>> Reference was vivified at -e line 1.
>> #line 1  -e
>> $y = undef
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>
>
> There appears to be no way to suppress the autovivification silently.
> Regardless, including "store" in the options also prevents autovivification
> which might be wanted:
>
> $ perl -MData::Dx -e 'no autovivification qw[fetch exists delete store
>> warn]; sub foo { return $_[0]; }; my $x = { foo => { bar => {} } }; Dx $x;
>> my $y = foo($x->{foo}{bar}{testing}{autovivification}{pragma}); Dx $y; Dx
>> $x; $x->{foo}{bar}{testing}{autovivification}{pragma} = 1;'
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>> Reference was vivified at -e line 1.
>> Reference was vivified at -e line 1.
>> #line 1  -e
>> $y = undef
>> #line 1  -e
>> $x = { foo => { bar => {} } }
>> Reference was vivified at -e line 1.
>> Reference was vivified at -e line 1.
>> Modification of a read-only value attempted at -e line 1.
>
>
> In this example, including "store" in the options is breaking the
> assignment at the end.
>
> Would this module have even been written if it weren't for the pitfalls of
> Perl's current autovivification behavior?  It seems to be intended as a
> tool to help fix code that might break due to unintended autovivification.
> Such situations arise very easily with "read-only" autovivification, but if
> Perl did autovivification only when actually necessary, there would be much
> less value in disabling it.
>

Personally I don't have a big problem with the standard rules, I find them
quite consistent, I think a much bigger problem is the docs don't explain
what expressions are in LVALUE context and what are in RVALUE context. I
think we could do much better there. Most people don't understand that the
list expression in a for (LIST) is in LVALUE context, as are the list
components of map, grep and procedure arguments. They must do so or the
aliasing effects of the loop iterators and @_ would not work.

I don't mind if we support other rules optionally if they can be made
efficient and we can completely explain the consequences but I really don't
think we can change the default behaviour. If the example above stopped
working far more code would break than I think you realize.

$_ = 1 for $hash{X};

Must be equivalent to

$hash{X}=1;

Cheers,
Yves

Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About