For the first time in more than five years, I got bit by autovivification. It’s one of those odd quirks of Perl that I’d read about, and heard the problems with but never bumped in to them.
I had put together a chunk of code using a hashref, which differentiated between the hash being undefined and being empty.
My hashref was initialized:
my $hashref = undef;
Code read:
return if exists $hashref->{something};
load_hashref($hashref) unless defined $hashref
See how that isn’t going to work? I see it now, but it took a few miutes of dinking around with Data::Dumper to figure out what was happening.
The use of $hashref->{something} autovivifies the hash in $hashref to {}, which messes up the undef check below it.
Now I have to decide if I’m going to change the code to check if the hash is empty rather than undef, or if I’m going to stick a “defined $hashref and” before the exists check and short-circuit the exists and autovivification.
Using {} will be more robust, so I should go that way.
How do you tell if a hash is empty, anyway? keys gives a list, and the list in scalar context is the number of items.
Code is now:
my $hashref = {};
return if exists $hashref->{something};load_hashref($hashref) unless scalar keys %$hashref;
Thus, nothing will come along and screw up the code by accidentally referring to the hash.
Yay!
Tags: Perl
The last line can even be simpler:
load_hashref($hashref) unless %$hashref;
Best regards,
you shouldn’t need scalar keys
my $hashref = {};
return if exists $hashref->{something};
load_hashref($hashref) unless %$hashref;
I was following the advice from http://www.perlmonks.org/?node_id=173677 which points out that not all tied hashes behave properly in those situations.
In my specific case, it would have been fine, but in the general case it might not always work.
you don’t need to use scalar keys, just using a hash in scalar context would work as well
I like
return if $hashref and exists $hashref->{something};
much better than counting the keys.
Even if you stick with
keys
, you don’t needscalar
.But I would add a clause to prevent autovivification instead of permitting an empty hash.
Note that a normal reference can never be a false value, so you don’t really need
defined
, just checking for truthiness is fine.Bottom line, I’d write:
return if $hashref and $hashref->{something};
load_hashref $hashref if not $hashref;
What happens if you’ve already called load_hashref earlier, and it loaded an empty hash? Should you call load_hashref again this time around, or not?
I think that argues for guarding against autovivification, and in favor of distinguishing between 2 different states:
$hashref is defined: means load_hashref has already been called, even if it loaded 0 keys
$hashref is not defined: means load_hashref hasn’t been called yet.