Re: Are defined or // garantee not to autovivify?
by haukex (Archbishop) on May 29, 2020 at 16:22 UTC
|
Autovivification is a little underdocumented, IMHO. The Camel generally only talks about autovivification in three circumstances: for file/dirhandles, references, and/or lvalue context. I don't believe defined provides lvalue context, and // does not, and you're working with a plain hash, not a reference, so I think that none of the three circumstances really apply. Also, even with autovivification in play, my $r; print $r->{x}; causes $r to become a hash reference, but the key does not spring into existence. In this node, dave_the_m even explained how hash accesses in subroutine arguments are special-cased not to autovivify, despite the lvalue context.
So despite the absense of clear specifications, I believe the examples you showed are safe, and because Perl is known for its backward compatibility, I would be very surprised (and somewhat worried) if a future version of Perl were to change this, and I think it'd probably break a lot of existing code. Personally, I expect simply checking a hash key to not autovivify - but again, only outside of an lvalue context, and only as the last element in a chain of dereferencing, if any.
(One option if you have doubts and want to protect against future changes and code super defensively: just stick this line at the top of your script: { my %x; die if defined $x{y}; die if keys %x } # verify that autovificiation behavior hasn't changed.)
| [reply] [d/l] [select] |
Re: Are defined or // guaranteed not to autovivify?
by Athanasius (Archbishop) on May 29, 2020 at 14:41 UTC
|
Hello NisSam, and welcome to the Monastery!
Further to hippo’s answer, I just want to point out that even using exists doesn’t ensure that autovivification won’t occur:
0:33 >perl -Mstrict -MData::Dump -wE "my %h = (A => 'a'); if (exists
+$h{B}{C}) { say 'yes'; }; dd %h;"
("B", {}, "A", "a")
0:33 >perl -Mstrict -MData::Dump -wE "no autovivification; my %h = (A
+ => 'a'); if (exists $h{B}{C}) { say 'yes'; }; dd %h;"
("A", "a")
0:33 >
— as explained in the documentation for the exists function. So, as hippo says, using no autovivification; is the safest option.
Hope that helps,
| [reply] [d/l] [select] |
Re: Are defined or // garantee not to autovivify?
by hippo (Archbishop) on May 29, 2020 at 14:20 UTC
|
I want to be sure that this behavior will not change in the future.
I doubt you will get such a guarantee - the language evolves. If you don't want autovivification, why not just put
no autovivification;
in your code and then it's all done. That would be my best advice to do what you can to prevent it now and in the future. See autovivification for more options. | [reply] [d/l] |
|
|
$ perl -e'
my %h;
1 for $h{k};
CORE::say 0+keys(%h);
'
1
$ perl -e'
no autovivification;
my %h;
1 for $h{k};
CORE::say 0+keys(%h);
'
1
| [reply] [d/l] |
Re: Are defined or // garantee not to autovivify?
by ikegami (Patriarch) on May 29, 2020 at 22:13 UTC
|
Hash elements are created when accessed in lvalue context. Here are examples of hash values used in lvalue context:
$hash{$key} = ...;
++$hash{$key};
for ($hash{$key}) { ... }
\$hash{$key}
some_sub($hash{$key}) # Exception!! See footnote.
// doesn't create an lvalue context for its operands, not even if it's evaluated in an lvalue context itself.
$ perl -e'my %h; 1 for $h{k}; CORE::say 0+keys(%h)'
1
$ perl -e'my %h; 1 for $x // $h{k}; CORE::say 0+keys(%h)'
0
When passing a hash value to a sub, Perl specifically avoids creating the hash element until necessary. This means that some_sub($hash{$key}) doesn't create $hash{$key} unless the sub does something like $_[0] = "abc";.
| [reply] [d/l] [select] |
Re: Are defined or // garantee not to autovivify?
by jcb (Parson) on May 29, 2020 at 23:37 UTC
|
As other monks have pointed out, autovivification does not occur in the contexts you mention. Evaluating defined $hastest{abc}{def} will autovivify $hashtest{abc} (because you assumed the existence of a hash in that location in which to check for the key def), but will not autovivify def (because you tested whether that key specifically has a defined value).
Autovivification only occurs where the code assumes the existence of an aggregate and attempts to look up a value. Instead of throwing an error, Perl creates the implied aggregate.
| [reply] [d/l] [select] |
Re: Are defined or // garantee not to autovivify?
by perlfan (Parson) on May 31, 2020 at 05:58 UTC
|
I don't use exists $hashtest{abc} and defined $hashtest{abc} because defined can handle undef. However, sometimes I do defined $hashtest{abc} and $hashtest{abc} because I want the false condition to be based on more than $hashtest{abc} being undef. I also want it to evaluate to false if it's an empty string or 0, in addition to undef. In that case because the second condition is not protected by defined I need to make sure that the key exists. TBH I don't remember the last time I even used exists.
To summarize:
# generally pointless, I would not use the mere existence of the key t
+o mean anything; check it's value
if ( exists $hashtest{abc} and defined $hashtest{abc} ) {
print qq{This is worthless IMO.\n};
}
Rather just do this if undef is taken as does not exist.
if (defined $hashtest{abc}) {
print qq{Better than 'exists'; check for the value being undef or no
+t undef.\n};
}
And if I want to check the value, something like:
if (defined $hashtest{abc} and $hashtest{abc}) {
print qq{Also, better than 'exists' and when I want to make sure the
+ value is not "falsey"\n};
}
| [reply] [d/l] [select] |
|
|
I do defined $hashtest{abc} and $hashtest{abc} because I want the false condition to be based on more than $hashtest{abc} being undef.
In that case, simply using $hashtest{abc} in boolean context is enough to cover undef as well. (Personally I'd add the defined test if I'm going to be using an operator that will give me a warning when used with an undef value.)
I would not use the mere existence of the key to mean anything
The difference between defined and exists becomes quite significant if one iterates over the hash (keys, values, each).
| [reply] [d/l] [select] |