Alright, some people really love programming languages which are dynamically typed. Some say that statically typed is the only way. I do not know which camp I belong to. Or do I even want to belong any. How ever I found crazy thing which happened with dynamically typed language.
What it actually means when language is dynamic typed or static typed. I am not the top expert to answer to this question but in the nutshell it is something like…
In dynamically typed language you can declare variable like this:
$number = 1; $word = "something";
In statically typed language you have to declare variable like this:
int $number = 1; String $word = "something";
Here comes the crazy thing I promised
If you have ever developed with Perl you know that in Perl you can do many things like you want. Perl is dynamically typed language. I was adding some functionality into a already existing piece of code. There was already many checks and if something failed – error message was appended to $errors variable.
I added this block to that existing code:
if ( $errors->{what_ever} ){ # Do something }
That block broke everything. And it did it the way I could never expect. Plus there was absolutely no errors(even that strict and warnings exists). Here are two proof of concepts:
1. This is similar to my situation where everything failed.
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $errors; print Dumper($errors); # $VAR1 = undef; if( $errors ){ # This is not true print "This is not true\n"; } if ( $errors->{what_ever} ){ # Do something } print Dumper($errors); # $VAR1 = {}; if( $errors ){ # This is true print "This is true\n"; }
2. This is the valid version which works.
#!/usr/bin/perl use strict; use warnings; use Data::Dumper; my $errors = {}; print Dumper($errors); # $VAR1 = {}; if( $errors ){ # This is true print "This is true\n"; } if ( $errors->{what_ever} ){ # Do something } print Dumper($errors); # $VAR1 = {}; if( keys %{$errors} ){ # This is not true print "This is not true\n"; }
So what really happened in that first piece of code?
First I declared variable
my $errors;
It tells to Perl that “hey I want to use scalar called errors, please let me”.
Then little bit later comes the block:
if ( $errors->{what_ever} ){ # Do something }
There I asked “dear Perl do you remember that scalar called errors, please test if that scalar hash key called what_ever”. Then Perl thinks “Sure I remember it and sure I can test it. Oh wait, scalars do not have keys. Only hashes have keys. Well I make this errors variable hash”. Here is the big deal. Perl defines $errors to be hash. I could never expect it will do it in if statement.
Then code runs forward and comes
if( $errors ){ # This is true print "This is true\n"; }
Here I am asking: “Please Perl one more question: Can you tell if $errors is true”. Perl answers: “Of course it is true, it is the hash”. What I expected here is that perl says “It is false because there is nothing in there”.
It’s called autovivification. You can turn off that “feature” using the autovivification module from CPAN.
Thank you for the comment!
Yeah I see this autovivification thing is brilliant. But still
I would not expect that it changes undefined to hash ref just by saying:
if($errors->{something){}
Actually it changes even if I say
warn $errors->{something};
I totally understand it if I say:
$errors->{something} = 1337;
There is nice article about this on PerlMaven: http://perlmaven.com/autovivification
Those examples make sence to me. But the thing I pointed out earlier does not. I think there is a huge difference.
Has nothing to do with autovivification. A reference to a hash is a scalar which is true even if the hash is empty. You have confused a hash ref with a hash. Not the same.
Fwiw, like most such problems you can find in Perl 5, this has been fixed to work as you would understand it in Perl 6:
[S]ubscripting itself does not cause autovivification: It only happens when the result of the subscripting chain is assigned to (or otherwise mutated).
(from https://docs.perl6.org/language/subscripts#Autovivification)
This is a bit of a Perl oddity. But if you know you’re going to be using a hash to store something, declare a hash instead of a scalar:
“my %errors;”
This prevents the issue you had, because “if (%hash)” only returns true if there are items in the hash.
What you are doing is using a scalar variable, which Perl turns into a reference to a hash when you use it like a hash (in your “if” statement). Perl is very good at trying to “do what I mean” (DWIM) and will make inferences like this at times. So in your case, the scalar “$errors” was then a reference to a hash and therefore no longer undefined or “false”.
Because of Perl is dynamically typed language When you do: $errors->{ what_ever } you say that $errors should be hash ref then you check ‘what_ever’.
That work both cases: on read and on write.
But I will agree with you that it is not expected autovivification to work when you just read.
Writing if( $errors->{ what_ever } ) I just actually check if( ref $errors eq ‘HASHREF’ && $errors->{ what_ever } )
I agree that the autovivification has unexpected side-effects, because they are triggered even by a read read-only operation (such as a test), but the problem in your code arises more as a consequence of something which is nearly always a mistake (in any programming language), that is, testing a reference. In your simple case of a single-level hash, if you have tested the hash instead of the reference (that is %$errors instead of $errors), you would have had no unexpected results (because %$errors is an empty hash). Here is the code to show that:
—
my $errors;
if ( $errors->{what_ever} ) {
print “Key found!\n” # Prints nothing.
}
# 2nd attempt
if ( $errors->{what_ever} ) {
print “Key found!\n” # Prints nothing
}
if ( $errors ) {
print “Reference found!\n” # It prints.
}
if ( %$errors ) {
print “Non empty hash found!\n” # Prints nothing.
}
—
Initializing $errors like this:
my $errors = {};
would have been even clearer.
Yeah, To explain in a handful or two of words. your scalar started undefined (and thus false), then referenced it as though it WERE defined — defined as a reference to a hash. That has was created, the (scalar) reference is now *defined* (to point to the hash). No longer undefined, it is thus not false.
Understand that scalars can be references, and don’t confuse references with what they reference.