7

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?

Community
  • 1
  • 1
Zaid
  • 36,680
  • 16
  • 86
  • 155
  • 4
    You can turn off autovivification for any lexical scope using "no [autovivification](http://search.cpan.org/perldoc?autovivification)". – rafl Oct 20 '10 at 12:59
  • My question was more about the mutable behavior of constants with `exists` rather than how I could avoid it. – Zaid Oct 20 '10 at 13:12
  • 3
    when working with constants, remember that `use constant PI => 3.14` is the same as `sub PI () {3.14}` and `use constant data => {...}` is `{my $data = {...}; sub data () {$data}}` – Eric Strom Oct 20 '10 at 14:48

2 Answers2

16

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.

Dave Cross
  • 68,119
  • 3
  • 51
  • 97
  • Could you explain why Perl throws a `Can't modify constant item in scalar assignment` error for `use constant var => 50; var = 40;` – Zaid Oct 20 '10 at 10:27
  • 2
    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. – Dave Cross Oct 20 '10 at 11:27
  • Good stuff, wish I could +2 this answer. – Zaid Oct 20 '10 at 11:34
9

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 Yarmash
  • 142,882
  • 41
  • 325
  • 378
  • Is a constant hashref a scalar constant? – Zaid Oct 20 '10 at 10:07
  • 1
    I don't see how this is an answer to the question? – ysth Oct 20 '10 at 13:12
  • Use something like Data::Lock (part of Attribute::Constant) rather than Readonly. – MkV Oct 20 '10 at 13:23
  • 4
    @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 Oct 20 '10 at 13:42
  • 1
    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 Oct 21 '10 at 01:58