http://qs321.pair.com?node_id=221679

adrianh has asked for the wisdom of the Perl Monks concerning the following question:

Due too some of the discussion on Class::InsideOut I have discovered that I am missing some of the subtleties of perl's lexical pads, and their interaction with package-specific attributes.

Basically, lexical pads don't seem to be as localised as I thought they would be - and attribute subroutines seem to be being called in an odd context.

(Background: I am largely unfamiliar with the perl internals. I've skimmed perlguts and related docs but can't find anything obvious that answers my questions. However, I've worked on compilers for other languages and am familiar with the basic concepts.)

My naive concept of perl's lexical pads that there would be one associated with each lexical scope something like this.

# new pad here for the file's scope { # new pad here for the block's scope } sub foo { # new pad here for the subroutine's scope } ... etc ...

and from my reading of attributes in perl 5.8 doing.

my %foo : Bar = (answer => 42);

should be the same as

use attributes (); my %foo; attributes::->import(__PACKAGE__, \%z, 'Bent'); %foo = (answer => 42);

However, some experiments with PadWalker show my understanding is in error:

use strict; use warnings; package AttrTest; use Data::Dumper; use PadWalker qw(peek_my); sub MODIFY_HASH_ATTRIBUTES { my ($package, $reference, @attributes) = @_; $package->dump_lex("setting @attributes in $package for $reference +"); return; }; sub dump_lex { my ($class, $when) = @_; print "when $when top-level pad is\n"; my $level=0; while (eval {peek_my(++$level)} && !$@) {}; my $hash = peek_my($level-1); while (my ($name, $value) = each %$hash) { my $dumped = Data::Dumper->new([\$value],[$name])->Indent(0)-> +Dump; print "\t$value -> $dumped\n"; }; print "\n"; }; INIT { AttrTest->dump_lex('init') }; { package Foo; use base qw(AttrTest); my %foo : Attr = (one => 1); }; { package Bar; use base qw(AttrTest); use attributes (); my %bar; attributes::->import(__PACKAGE__, \%bar, 'Attr'); } AttrTest->dump_lex('runtime');

Under perl 5.6 this produces

when setting Attr in Foo for HASH(0x8187bec) top-level pad is + SCALAR(0x8186018) -> $%foo = \\'%foo'; + + when init top-level pad is + HASH(0x818bf68) -> $%bar = \{}; + HASH(0x8187bec) -> $%foo = \{}; + + when setting Attr in Bar for HASH(0x818bf68) top-level pad is + HASH(0x818bf68) -> $%bar = \{}; + + when runtime top-level pad is

Under perl 5.8 this produces

when init top-level pad is HASH(0x22dda8) -> $%bar = \{}; HASH(0x3cff4) -> $%foo = \{}; when setting Attr in Foo for HASH(0x3cff4) top-level pad is when setting Attr in Bar for HASH(0x22dda8) top-level pad is HASH(0x22dda8) -> $%bar = \{}; when runtime top-level pad is

Both examples used the same version of PadWalker (v0.08). I am also aware of the change in 5.8 that means attributes declared with my are now applied at runtime.

This output confuses me for a couple of reasons:

  • Why can INIT see %foo and %bar? They are not in the scope of the INIT subroutine and, since they are in their own blocks, they should not be visible from the file-scoped pad (the last call to dump_lex doesn't show them)?
  • Why can't the implicit call to MODIFY_HASH_ATTRIBUTES in Foo see %foo in perl 5.8? The attribute routine is being called at runtime so the hash should have been declared and be in the pad?

I realise that package-specific attributes are still considered experimental - so I'm not particularly worried about the different behaviour - but I would like to understand what's going on :-)

Can any perl internals guru's explain to this poor confused soul?