views:

78

answers:

1

I'm trying to understand the behavior of the fields pragma, which I find poorly documented, regarding fields prefixed with underscores. This is what the documentation has to say about it:

Field names that start with an underscore character are made private to the class and are not visible to subclasses. Inherited fields can be overridden but will generate a warning if used together with the -w switch.

This is not consistent with its actual behavior, according to my test, below. Not only are _-prefixed fields visible within a subclass, they are visible within foreign classes as well (unless I don't get what 'visible' means). Also, directly accessing the restricted hash works fine.

Where can I find more about the behavior of the fields pragma, short of going at the source code?

{
    package Foo;
    use strict;
    use warnings;
    use fields qw/a _b __c/;

    sub new {
        my ( $class ) = @_;
        my Foo $self = fields::new($class);
        $self->a = 1; $self->b = 2; $self->c = 3;
        return $self;
    }

    sub a : lvalue { shift->{a}   }
    sub b : lvalue { shift->{_b}  }
    sub c : lvalue { shift->{__c} }
}
{
    package Bar;
    use base 'Foo';
    use strict;
    use warnings;
    use Data::Dumper;

    my $o = Bar->new;
    print Dumper $o; ##$VAR1 = bless({'_b' => 2, '__c' => 3, 'a' => 1}, 'Foo');

    $o->a = 4; $o->b = 5; $o->c = 6;
    print Dumper $o; ##$VAR1 = bless({'_b' => 5, '__c' => 6, 'a' => 4}, 'Foo');

    $o->{a} = 7; $o->{_b} = 8; $o->{__c} = 9;
    print Dumper $o; ##$VAR1 = bless({'_b' => 8, '__c' => 9, 'a' => 7}, 'Foo');
}
+7  A: 

Coincidentally enough, I happen to have a test script in ~/codescraps/fields/test.pl dated from two years ago when I experimented with answering exactly this same question. :)

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

{
   package Foo;
   use fields qw(foo bar _Foo_private);
   use private qw(_really_private);
   sub new {
       my Foo $self = shift;
       unless (ref $self) {
           $self = fields::new($self);
           $self->{_Foo_private} = "this is Foo's secret";
       }
       $self->{foo} = 10;
       $self->{bar} = 20;
       return $self;
   }
}

my $foo = Foo->new;
$foo->{foo} = 42;

# this will generate an error: field does not exist
#$foo->{zap} = 42;

print "_Foo_private: " . $foo->{_Foo_private} . "\n";
$foo->{_Foo_private} = 1;
print "_Foo_private: " . $foo->{_Foo_private} . "\n";

print "_really_private: " . $foo->{_really_private} . "\n";
$foo->{_really_private} = 1;
print "_really_private: " . $foo->{_really_private} . "\n";

print Dumper($foo);

# subclassing
{
   package Bar;
   use base 'Foo';
   use fields qw(baz _Bar_private);        # these fields not shared with Foo
   sub new {
       my $class = shift;
       my $self = fields::new($class);
       $self->SUPER::new();                # init base fields
       $self->{baz} = 10;                  # init own fields
       $self->{_Bar_private} = "this is Bar's secret";
       return $self;
   }
}

my $bar = Bar->new;
# these work fine
$bar->{foo} = 1;
$bar->{bar} = 1;
$bar->{_Bar_private} = 1;

# this will not work - underscored fields are not visible to children
$bar->{_Foo_private} = 1;

And when I run your code, I get the error:

No such pseudo-hash field "_b" at test2.pl line 16.

(line 16 is the definition for sub b.) What architecture are you running this on? Objects using the fields pragma aren't simple blessed hashrefs -- they are blessed arrayrefs, e.g. when I modify your constructor to look like this:

sub new {
    my ( $class ) = @_;
    my Foo $self = fields::new($class);
    $self->{a} = 1; $self->{_b} = 2; $self->{__c} = 3;
    print "I look like: ", Data::Dumper::Dumper($self);
    return $self;
}

I see:

I look like: $VAR1 = bless( [
                 bless( {
                          'a' => 1
                        }, 'pseudohash' ),
                 1,
                 2,
                 3
               ], 'Bar' );

As a postscript, I feel obliged to point out that the fields pragma, and the base pragma that goes with it, are both deprecated and one is strongly urged to avoid using them. Nowadays, if you're looking to construct a nice OO module with accessors, one would either use Class::Accessor or go directly to Moose.

Ether
Thanks for you reply. I'm running x86_64, debian and perl 5.10. I have to point out, as well, that as far as I know, `fields` is not deprecated at all. However, since 5.9, its implementation stopped using pseudo-hashes in favor of restricted hashes. And I, too, have a copy of Conways' OO Perl; I certainly know about the alternatives to 'fields' and 'base'. As I said, I just want to understand the pragmas, and the documentation is lacking.
Pedro Silva
@Pedro: aye, they'll be available for quite some time, but not really supported anymore; I actually asked a question here about `fields` myself, a while back: http://stackoverflow.com/questions/1168644/why-is-the-fields-pragma-incompatible-with-multiple-inheritance-in-perl -- and got the same "use Moose instead" replies. :)
Ether