sectokia has asked for the wisdom of the Perl Monks concerning the following question:
I thought I knew perl, and then this tripped me up:
result:use Data::Dumper qw(Dumper); my $a = { b => {} }; my $f = $a->{b}{c}{d}//undef; print Dumper $a
$VAR1 = { 'b' => { 'c' => {} } };
Why does 'c' key get created during what is meant to be only an assignment operation? And under that logic, why isn't 'd' made a key of 'c' with value undef?
|
---|
Replies are listed 'Best First'. | |
---|---|
Re: Arrow Operator Question
by swl (Prior) on Mar 26, 2023 at 09:17 UTC | |
It's autovivification. See for example https://perlmaven.com/autovivification. If you want to disable it then have a look at the autovivification module. | [reply] |
Re: Arrow Operator Question
by kcott (Archbishop) on Mar 26, 2023 at 11:45 UTC | |
G'day sectokia, [Note: $a (and $b) are special variables; it's best to only use these for their intended purposes to avoid unexpected side effects; I've replaced $a with $h in the code below. Also, '//undef' is completely superfluous: if the LHS is undef, use undef instead. :-)] See Autovivification and Arrow Notation in perlref. Perl will autovivify as needed. It requires 'c' to check for 'd', so that key is autovivified. The 'd' key either exists and has a value or it doesn't exist and the value is undef: no autovivification is needed here. You can use exists() to check for 'c'; only attempting to get a value for 'd' if 'c' exists. That can become unwieldy when there are multiple levels of keys; if you think autovivification is a problem, simply allow it then delete() afterwards. The following code has examples which demonstrate these points.
Output:
— Ken | [reply] [d/l] [select] |
by BillKSmith (Monsignor) on Mar 27, 2023 at 14:24 UTC | |
OUTPUT:
Bill
| [reply] [d/l] [select] |
by hv (Prior) on Mar 27, 2023 at 17:28 UTC | |
This is dangerous: the nature of my $var = ... if ... is predictable but very noninuitive (it acts something like a state variable). It has long been regarded as a bug that perl core would dearly love to fix, but cannot due to back-compat. I recommend always either splitting out the declaration from the assignment:
.. or using state explicitly when that's what you actually intend:
| [reply] [d/l] [select] |
by BillKSmith (Monsignor) on Mar 28, 2023 at 18:07 UTC | |
by kcott (Archbishop) on Mar 27, 2023 at 16:57 UTC | |
G'day Bill, That's certainly a valid way to go. Consider the following:
Update: The code above is a modification of the original. Something was niggling me about what I first wrote, but I couldn't see the problem. ++hv's response to your post alerted me to the issue. My first, less-than-good effort is in the spoiler below. Note that the output is unchanged.
Output:
I think each has its merits and probably comes down to best choice on a case by case basis. — Ken | [reply] [d/l] [select] |
by BillKSmith (Monsignor) on Mar 28, 2023 at 18:02 UTC | |
by sectokia (Friar) on Mar 27, 2023 at 08:41 UTC | |
Thanks. The reason I had undef was the style I generally use is more like this: if ($foo == ($bar->{c}{d}{e}//'moo')) { ... } | [reply] [d/l] |
by kcott (Archbishop) on Mar 27, 2023 at 10:14 UTC | |
"if ($foo == ($bar->{c}{d}{e}//'moo')) { ... }" You may have typed that in a hurry. While I do follow the gist of what you're saying, I do hope you're aware that '==' is used for numbers (e.g. 300) and 'eq' is used for strings (e.g. "moo"). See "perlop: Equality Operators" for more complete details. — Ken | [reply] [d/l] [select] |
Re: Arrow Operator Question
by tobyink (Canon) on Mar 26, 2023 at 11:54 UTC | |
As others have said, it's autovivification. As a quick way to explain why the 'c' key gets created, but not 'd' though: $a->{b}{c}{d} is looking for a 'd' key within $a->{b}{c}, so you are asserting that $a->{b}{c} must be a hashref for your program to succeed at all. So Perl makes it a hashref for you. But you're not asserting anything in particular about the nature of $a->{b}{c}{d}, so Perl doesn't make it anything. | [reply] [d/l] [select] |
Re: Arrow Operator Question (Autovivification motivated)
by LanX (Saint) on Mar 26, 2023 at 13:12 UTC | |
But it seems nobody explained "why". It's DWIM. If you try the same thing in JS you get an error. in the browser's console (F12)
Assigning to a deeply nested path is even more a PITA in those languages
You'll need a loop to build each level step by step. But Perl does DWIM by creating accessed levels on the fly.
Many paint this as bug, because it's different to other languages. I disagree, it's a case where you can't make an omelet without breaking an egg somewhere. And I profit from this far more often than I stumble over it. It should be be better explained and motivated though.
metaThere is no autovivification to disable this on demand I personally think a syntactic solution with a no-autovivification operator might have been better. Alas there are not many characters left in the alphabet, and I'm not sure such a syntax could work: $a->{b}:{c}{d} # should not vivify beneath b FWIW there is also Data::Diver (and other similar modules) on CPAN, which could also be core.
updateBTW: Contrary to the title, you don't need the arrow operator to see this effect.
updateI'd love to see other or even better motivations for "Autovivification" discussed. Cheers Rolf | [reply] [d/l] [select] |