Perl hash of hashes rationale

Viewed 111

I have decided to give Perl a try and I have stumbled across a language structure that seems to be valid, but I just can't believe it is. As I guess there is some rationale behind this I decided to ask a question.

Take a following Perl code:

%data = ('John Paul' => ('Age' => 45), 'Lisa' => 30);
print "\$data{'John Paul'} = $data{'John Paul'}{'Age'}\n";
print "\$data{'Lisa'} = $data{'Lisa'}\n";

My intention was to check how hash of hashes works. The above code prints:

$data{'John Paul'} =
$data{'Lisa'} =

To make it a valid hash of hashes one needs:

%data = ('John Paul' => {'Age' => 45}, 'Lisa' => 30);

and the result would be:

$data{'John Paul'} = 45
$data{'Lisa'} = 30

Does anyone know:

  1. Why there is non uniformity and the internal hash needs {} instead of ()?
  2. Why do I get no error or warning that something is wrong when there is () instead of {} for the internal hash? It is very easy to do such kind of mistakes. What is more, ('Age' => 45) breaks not only the value for 'John Paul' but also for 'Lisa'. I just can't imagine searching for such kind of "bugs" in project with thousands lines of code.
2 Answers
( 'John Paul' => ( 'Age' => 45 ), 'Lisa' => 30 )

is just another way of writing

'John Paul', 'Age', 45, 'Lisa', 30

Parens don't create any data structure; they just affect precedence like in (3+4)*5. The reason we don't write

my %h = a => 4;

or the equivalent

my %h = 'a', 4;

is that it would be interpreted as

( my %h = 'a' ), 4;

What creates the hash is my %data, not the parens. The right-hand side of the assignment just places an arbitrary number of scalars on the stack, not a hash. The assignment operator adds these scalars to the hash.


But sometimes, we want to create an anonymous hash. This is where {} comes in.

my %data = ( 'John Paul' => { 'Age' => 45 }, 'Lisa' => 30 );

is basically equivalent to

my %anon = ( 'Age' => 45 );
my %data = ( 'John Paul' => \%anon, 'Lisa' => 30 );

Note that \%anon returns a scalar, a reference to a hash. This is fundamentally different than what ( 'John Paul' => \%anon, 'Lisa' => 30 ) and 'John Paul' => \%anon, 'Lisa' => 30 return, four scalars.


Why there is non uniformity and the internal hash needs {} instead of ()?

An underlying premise of this question is false: Hashes don't need (). For example, the following are perfectly valid:

my %h1 = 'm'..'p';
sub f { return x => 4, y => 5 }
my %h2 = f();
my %h3 = do { i => 6, j => 7 };

() has nothing to do with hashes. The lack of uniformity comes from the lack of parallel. One uses {} to create a hash. One uses () to override precedence.

Since parens just affect precedence, one could use

my %data = ( 'John Paul' => ({ 'Age' => 45 }), 'Lisa' => 30 );  # ok (but weird)

This is very different than the following:

my %data = ( 'John Paul' => ( 'Age' => 45 ), 'Lisa' => 30 );  # XXX

Why do I get no error or warning that something is wrong when there is () instead of {} for the internal hash?

Not only is using () valid, using () around expressions that contain commas is commonly needed. So when exactly should it warn? The point is that it's arguable whether this should be a warning or something perlcritic finds, at least at first glance. The latter should definitely find this, but I wouldn't know if a rule for it exists or not.

Why there is non uniformity and the internal hash needs {} instead of ()?

An assignment to a hash is a list of scalars (alternating between keys and values).

You can't have a hash (because it isn't a scalar) as a value there, but you can have a hash reference.

Lists get flattened.

Why do I get no error or warning that something is wrong when there is () instead of {} for the internal hash?

Because you didn't turn them on with the use strict; use warnings; pragmas (which are off by default for reasons of horrible backwards compatibility but which will be on by default in Perl 7).

Related