[% setvar title Objects : Private keys and methods %]
Note: these documents may be out of date. Do not use as reference! |
To see what is currently happening visit http://www.perl6.org/
Objects : Private keys and methods
Maintainer: Damian Conway <damian@conway.org> Date: 1 Sep 2000 Last Modified: 25 Sep 2000 Mailing List: perl6-language-objects@perl.org Number: 188 Version: 3 Status: Frozen
This RFC proposes two new keywords -- private
and public
-- that limit
the accessibility of keys in a hash, and of methods. Their primary use
would be to provide encapsulation of attributes and methods in
hash-based objects.
It is proposed that Perl 6 provide a unary function, 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 private
would return its single argument.
The effects of applying private
to a single hash entry would be to:
mark the entire hash as non-autovivifying (except via future calls to
private
or public
-- see below)
mark the particular entry as being restricted to the package in which the
call to private
occurs
mark any other existing entries of the hash as publicly accessible (see "Public accessibility" below).
After a hash has been marked in this way, the specific key(s) 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 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 private
-ized, the only way to extend its set of
entries is via another call to private
(or public
-- see below):
sub new { my ($class, %self) = @_; bless private \%self, $class; $self{seed} = rand; # dies, can't autovivify private $self{seed} = rand; # okay $self{seed} = rand; # now okay }
Applying private
to a hash slice would apply it individually to each
entry in the slice. Applying private
to an entire hash (directly, or via
a reference) would apply it to every entry in the hash that is not already
private (or public -- see below).
Private
entries and inheritancePrivate entries of hashes could be 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} = $derdatum; return $self; } sub data { return $_[0]->{data} }; # Derived::data
Note that this solves the pernicious "data collision" problem in OO Perl.
private
-ized hashesWhen a private
-ized hash is iterated -- via each
, keys
, or
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 private
isn't accidentally
subverted during iteration.
Although it is almost inevitably a Very Bad Idea and we shall probably
All Come Regret To It Later, it ought to be possible to specify that
certain entries of a private
-ized hash are nevertheless public. This
might, for example, be necessary when inheriting from legacy code.
To provide this capacity, a second built-in function -- public
--
would be provided. It would act in most respects like private
, but
with the following differences:
public
would be accessible from any package.public
would not be assocated with any particular
package (not even with the package in which they are declared), and could
not be qualified with a package name.If a hash has public entry for a given key, it can have no other entries -- neither public nor private -- with the same key.
In all other respects the public
keyword has analogous effects to
the private
keyword. Specifically:
public
function is applied is thereafter
non-autovivifying (except via another call to public
or a call to
private
).public
is applied to a normal (non-private, non-public) hash,
all the entries in the hash at that point are made public.The 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 private
causes it to be
ignored during subroutine or method dispatch unless:
The call originates in the same package, or
The call originates in a derived package and explicitly qualifies the subroutine name with the name of its owner package.
Note: an alternative would be to make private
a subroutine attribute:
sub check : private { ... }
However, it is felt that the modification private
imposes is so
significant that it warrants a prefix modifier. Besides, the private
keyword will be easier to explain and remember if it is consistently
used as a prefix for both hash entries and subroutines.
None.
The Tie::SecureHash module implements much the same idea.
Christiansen & Torkington, Perl Cookbook, pp. 470-472.
Conway, Object Oriented Perl, pp. 309-326.