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:
Deven T. Corzine
Date:
July 14, 2019 04:37
Subject:
Re: [perl #133809] Perl silently adds a key to hash
Message ID:
CAFVdu0QzhVUTL3GEW3ZNAC0bG9+q+air5YaOgUpcGLrNUW9Q4w@mail.gmail.com
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:

$ 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:

$ 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.

Deven

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