This and other RFCs are available on the web at
  http://dev.perl.org/rfc/

=head1 TITLE

Objects : Private keys and methods

=head1 VERSION

  Maintainer: Damian Conway <[EMAIL PROTECTED]>
  Date: 1 September 2000
  Mailing List: [EMAIL PROTECTED]
  Version: 1
  Number: 188
  Status: Developing

=head1 ABSTRACT

This RFC proposes a new keyword -- C<private> -- that limits the
accessibility of keys in a hash, and of methods. Its primary use would be to
provide encapsulation of attributes and methods in hash-based objects.

=head1 DESCRIPTION

=head2 Private hash entries

It is proposed that Perl 6 provide a unary function, C<private>, that
restricts hash entries to the current package. The keyword could be applied to
a single hash entry:

        private $hash{key};
        private $hash{$key};
        private $hashref->{key};

or to a hash slice:

        private @hash{qw(_name _rank _snum)};

or to a complete hash (either directly, or via a reference):

        private %hash;
        private { _name => "demo", _rank => "private", _snum => 123 };
        private bless { @attrs }, $class;
        bless private { @attrs }, $class;

In all cases, a call to C<private> would return its single argument.

The effects of applying C<private> to a single hash entry would be to:

=over 4

=item 1.

mark the entire hash as non-autovivifying (except via future calls to
C<private> -- see below)

=item 2.

mark the particular entry as being restricted to the package in which the
call to C<private> occurs

=back

After a hash has been marked in this way, the specific key would only be
directly accessible in the same package:

        package MyClass;
        
        sub new { bless private { secret => 'data' }, $_[0] }
        

        package main;
        
        my $obj = MyClass->new();
        print $obj->{secret};                   # dies, inaccessible entry


Attempts to autovivify keys of a C<private>-ized hash would also be fatal:

        package MyClass;
        
        sub print_me { print $_[0]->{sekret} }  # dies, no such entry

        
        package main;
        
        my $obj = MyClass->new();
        print $obj->{public};                   # dies, can't create entry


Once a hash has been C<private>-ized, the only way to extend its set of
entries is via another call to C<private>:

        sub new {
                my ($class, %self) = @_;
                bless private \%self, $class;
                private $self{seed} = rand;     # okay
                $self{seed} = rand;             # dies, can't autovivify
        }

        

Applying C<private> to a hash slice would apply it individually to each
entry in the slice. Applying C<private> to an entire hash (directly, or via
a reference) would apply it to every entry in the hash that is not already
private.


=head3 C<Private> entries and inheritance

Private entries of hashes could be I<indirectly> accessed in packages
that inherit from the entry's package, by qualifying (i.e. prefixing) the
key with the entry's package name. For example:

        package Base;
        
        sub new {
                my ($class, @data) = @_;
                bless private { data => [@data] }, $class;
        }
        
        
        package SortableBase;
        use base 'Base';
                
        sub sorted {
                my ($self) = @_;
                print sort @{ $self->{Base::data} };
        }
        
Note, however, that it would still be a fatal error to attempt to access a
private entry outside its package's hierarchy, even with full qualification:

        package main;
        
        my $obj = Base->new(@data);
        
        print $obj->{Base::data};               # dies, not in derived class



Because private entries are only directly acccessible within their own package,
a hash could be given two private entries with the same key, but associated 
with different packages. For example:

        package Base;
        
        sub new {
                my ($class, @data) = @_;
                bless private { data => [@data] }, $class;
        }
        
        sub data { return $_[0]->{data} };      # Base::data
        
        
        package Derived;
        use base 'Base';
                
        sub new {
                my ($class, $derdatum, @basedata) = @_;
                my $self = $class->SUPER::new(@basedata);
                private $self->{data} = $derdata;
                return $self;
        }

        sub data { return $_[0]->{data} };      # Derived::data
        

Note that this solves the pernicious "data collision" problem in OO Perl.


=head3 Iteration of C<private>-ized hashes

When a C<private>-ized hash is iterated -- via C<each>, C<keys>, or
C<values> -- the only keys or values returned would be those that are
directly or indirectly accessible within the current package. Furthermore,
iterators that return keys would always return fully qualified keys.

For example:

        package Base;
        
        sub new {
                my ($class) = @_;
                bless private { a=>1, b=>2 }, $class;
        }
        
        sub iterate {
                my ($obj) = @_;
                print join ", ", keys %$obj;
        }
        
        
        package Derived;
        use base 'Base';
                
        sub new {
                my ($class) = @_;
                my $self = $class->SUPER::new();
                private $self->{c} = 3;
                return $self;
        }

        sub iterate {
                my ($obj) = @_;
                print join ", ", keys %$obj;
        }


        package main;

        sub iterate {
                my ($obj) = @_;
                print join ", ", keys %$obj;
        }

        
        my $obj = Derived->new();
        
        Base::iterate($obj);            # prints: "Base::a, Base:b"
        Derived::iterate($obj);         # prints: "Base::a, Base:b, Derived::c"
        main::iterate($obj);            # prints: ""


This ensures that the encapsulation provided by C<private> isn't accidentally
subverted during iteration.     



=head2 Private methods

The C<private> keyword could also be applied to subroutines, to restrict
where they may be called. Access rules similar to those for private hash
entries would apply:

        package Base;
        
        sub new { ... }
        
        private sub check { ... }
        
        sub do_check {
                my ($self) = @_;
                $self->check();         # okay
                $self->Base::check();   # okay
                check();                # okay
                Base::check();          # okay
                
        
        package Derived;
        use base 'Base';
        
        sub do_check {
                my ($self) = @_;
                $self->check();         # dies, no suitable accessible method
                $self->Base::check();   # okay
                Base::check($self);     # okay
        }
                
        
        package main;
        
        my $obj = Base->new();
        $obj->check();                  # dies, no suitable accessible method
        $obj->Base::check();            # dies, no suitable accessible method
        Base::check($obj);              # dies, inaccessible subroutine


In other words, declaring a subroutine C<private> causes it to be
ignored during subroutine or method dispatch unless:

=over 4

=item 1.

The call originates in the same package, or

=item 2.

The call originates in a derived package and explicitly qualifies
the subroutine name with the name of its owner package.

=back

Note: an alternative would be to make C<private> a subroutine attribute:

        sub check : private { ... }

However, it is felt that the modification C<private> imposes is so
significant that it warrants a prefix modifier. Besides, the C<private>
keyword will be easier to explain and remember if it is consistently
used as a prefix for both hash entries and subroutines.
        


=head1 MIGRATION

No migration issues.

=head1 IMPLEMENTATION

The Tie::SecureHash module implements much the same idea.

=head1 REFERENCES

Christiansen & Torkington, I<Perl Cookbook>, pp. 470-472.

Conway, I<Object Oriented Perl>, pp. 309-326.

Reply via email to