for, map, grep and function calls autovivify arrayrefs
I was surprised by one aspect of Perl’s autovivification a few weeks ago, and I’m surprised it’s taken over 20 years for me to run into it.
I’m used to autovivification being a handy thing when working with, for
example, hashes of arrayrefs. Here I’m using autovivification to create
hash entries in %users_by_category
whenever a new one is added.
my %users_by_category;
for my $user ( @users ) {
my $category = categorize($user);
push @{$users_by_category{$category}}, $user;
}
This saves from having to do the tedious equivalent:
if ( not exists $users_by_category{$category} ) {
$users_by_category{$category} = [];
}
push $users_by_category{$category}, $user;
Autovivification doesn’t work just because you dereference an undef value. For example:
my $x;
my $n = scalar @{$x};
Can't use an undefined value as an ARRAY reference at ...
The push
in the first example works because using
@{$users_by_category{$category}}
in a push
makes it an “lvalue”,
meaning it’s a value that can appear on the left side of an assignment
operator, or something that can be assigned to.
What surprised me is that map
, grep
, for
and function calls are all lvalue context.
They’re lvalues because in all those cases, the value of the array being passed can be modified. It makes perfect sense when I thought about it, but I’d never run into it.
Here’s an example with grep
:
my $x;
grep {1} @{$x};
say "x is $x";
x is ARRAY(0x1e29fc8)
Or even just a regular function call:
sub foo {}
my $x;
foo(@{$x});
say "x is $x";
x is ARRAY(0x1059fc8)
To help protect against autovivification in your code when you’re not
expecting it, you can look at the
autovivification
pragma
available on CPAN.
Thanks to Leon Timmermans and Paul Evans for their input.