views:

135

answers:

2

The exists function can unexpectedly autovivify entries in hashes.

What surprises me is that this behavior carries over to constants as well:

use strict;
use warnings;
use Data::Dump 'dump';

use constant data => {
                       'foo' => {
                                  'bar' => 'baz',
                                },
                       'a'   => {
                                  'b'   => 'c',
                                }
                     };

dump data;   # Pre-modified

print "No data for 'soda->cola->pop'\n" unless exists data->{soda}{cola}{pop};

dump data;   # data->{soda}{cola} now sprung to life

Output

{ a => { b => "c" }, foo => { bar => "baz" } }
No data for 'soda->cola->pop'
{ a => { b => "c" }, foo => { bar => "baz" }, soda => { cola => {} } }

I suspect this is a bug. Is this something 5.10.1-specific, or do other versions of Perl behave similarly?

+8  A: 

You probably want to use Readonly for creating "true" constants.

Constants created using the constant pragma are actually inlinable subroutines. It means that at compile time the appropriate scalar constant is inserted directly in place of some subroutine call. If the constant is a reference, nothing prevents you from changing the data it points to.

eugene y
Is a constant hashref a scalar constant?
Zaid
I don't see how this is an answer to the question?
ysth
Use something like Data::Lock (part of Attribute::Constant) rather than Readonly.
MkV
@ysth: I think it's a good answer, albeit not one that spells everything out. Once you know that `constant` creates a subroutine that always returns the same value it follows that it's the hash reference it returns that's constant, not the hash itself. Since the hash is mutable, `exists` can trigger autovivification.
Michael Carman
The problem is with constant. It doesn't do what people expect in these cases. When you're using a tool that doesn't do what you need, you find one that does. That's why this answers the real need instead of the stated question.
brian d foy
+13  A: 

This is documented behaviour. perldoc constant says:

Even though a reference may be declared as a constant, the reference may point to data which may be changed, as this code shows.

use constant ARRAY => [ 1,2,3,4 ];
print ARRAY->[1];
ARRAY->[1] = " be changed";
print ARRAY->[1];

It's the reference that is constant, not what it refers to.

davorg
@davorg : Could you explain why Perl throws a `Can't modify constant item in scalar assignment` error for `use constant var => 50; var = 40;`
Zaid
Because you're trying to change a constant. And you can't do that. Constants are... well... constant. That's the whole point of them. The scalar value that you store in a constant can't be changed. But when you create a constant from a hash reference (as in your example) it's the reference that is fixed, not the data that you have a reference to. The reference is the scalar value which is stored in the constant.
davorg
Good stuff, wish I could +2 this answer.
Zaid