NisSam has asked for the wisdom of the Perl Monks concerning the following question:

Hello all.

I have a question about hash autovivification. I can check how it really works but I want to be sure that this behavior will not change in the future. So I need some reference to documentation.

If we have hash variable %hashtest, is it always guaranteed that defined($hashtest{abc}) or $hashtest{abc} // '?' will not autovivify for not existing keys? Or one have to always use exists($hashtest{abc}) && defined($hashtest{abc}) mantra?

  • Comment on Are defined or // garantee not to autovivify?

Replies are listed 'Best First'.
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.)

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,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

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.

      autovivification won't help because the OP isn't talking about the what we normally call autovivificaion (the creation of a referenced variable through dereference of an undefined value).

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

    1. 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";.

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.

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}; }
      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).