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

=head1 TITLE

Class Collections: Provide the ability to overload classes

=head1 VERSION

  Maintainer: Greg Williams <[EMAIL PROTECTED]>
  Date: 17 Sep 2000
  Last Modified: 1 Oct 2000
  Mailing List: [EMAIL PROTECTED]
  Number: 254
  Version: 2
  Status: Frozen

=head1 ABSTRACT

Currently, class methods can be overloaded through sub-classing. This
functionality is perhaps the most compelling aspect object oriented
programming offers.  This mechanism allows one to extend the functionality
of pre-existing code without significant copying or rewriting.  However,
there exists situations in which it is desirable not to extend methods, but
entire classes.

It is proposed that classes be able to be grouped into collections.
Collections would have the ability to inherit from other collections just
as classes inherit from other classes.  A sub-collection would overload
super-collection classes exactly like subclasses overload superclass
methods.

=head1 NOTES ON FREEZE

There was very little discussion of this RFC.  Damian Conway was the single
opponent to this RFC suggesting that the problem is better solved in other
ways (see below).  Michael G Schwern noted that the DBI suffers from the
problem described here (with respect to the interaction of DBI and
DBI::st).  Nathan Wiger and David L. Nicol both seemed to support the idea
of fixing this problem, both contemplating different ways to go about it.

Although there has been little discussion about this RFC, and has neither
ended in any agreement or strong conclusion, I am freezing the RFC so as
not to lose it to the RFC deadline. I hope that after being reviewed by
Larry Wall and others, it may be discussed in more depth at a later point
in time.

=head1 DESCRIPTION

In a typical HASA relationship, the method call to construct the
encapsulated object must make the call against either a class name, or a
scalar containing the class name.  In either case the name of the class
must be known and hard coded in some part of the code.

Consider two classes, Forest and Frog (both of which you found on CPAN).  A
Forest object HASA Frog object.  The Frog object has a method C<speak>
which prints the noise a frog makes. The Forest object has a method
C<make_noise> which prints a noise you might hear in the forest (like a
frog!):

     package Forest;
     sub new {
       my $class = shift;
       my $frog = Frog->new;
       return bless( { frog => $frog }, $class );
     }

     sub make_noise {
       my $self = shift;
       $self->{frog}->speak; # prints "ribbit ribbit";
     }

     package Frog;
     sub new {
       my $class = shift;
       return bless( {}, $class );
     }

     sub speak () { print "ribbit ribbit"; }
     sub jump ();
     sub croak (); # ;-)

In English this is fine, but now let's say you need the Forest class to
work in Japanese as well (Japanese frogs 'kerokero', they don't 'ribbit').
The Frog class is maintained by another developer however, and you'd rather
not make changes to it directly.  You can subclass Frog and provide your
own C<speak> method like so:

     package Frog::Japanese;
     @ISA = qw( Frog );
     sub speak { print "kerokero"; }

Here's where the problem lies.  Even though we now have a subclass of Frog,
the Forest class is still referencing the original Frog class and not
Frog::Japanese.  In order for Forest to reference Frog::Japanese, you could
edit the Forest class and change all occurances of Frog to Frog::Japanese,
but that is probably not wise.  Firstly, although you may be using a
relativly small subset of the features of Forest, the code may contain many
references to the Frog class, each of which you must now change.  Secondly,
and most importantly, by maintaining a locally modified version of Forest
you make it difficult to upgrade Forest from CPAN in the future (this
rationale just stopped you from making changes to the Frog class, after
all).

Assuming you did change your copy of Forest, you'd end up with a C<new>
constructor similar to this:

     package Forest;
     sub new {
       my $class = shift;
       my $frog = Frog::Japanese->new;
       return bless( { frog => $frog }, $class );
     }

Now the Forest is specifically Japanese though, and can't be used in
English anymore!  In perl 5, a logical solution is to subclass Forest for a
specific locale, with each subclass specifying which language (class) to
use:

     package Forest::Japanese;
     @ISA = qw( Forest );
     sub frog_class { 'Frog::Japanese' };

     package Forest::English;
     @ISA = qw( Forest );
     sub frog_class { 'Frog' };

     package Forest;
     sub new {
       my $class = shift;
       my $frog = $class->frog_class->new;
       return bless( { frog => $frog }, $class );
     }

At this point, we must stop and question whether all this work should be
necessary just to change one return value of one method.  A better solution
(albeit not currently possible) would be to 'overload' the Frog class with
a new Japanese version (also named 'Frog').  The Japanese version of the
Frog class would inherit (in a slightly altered sense) everything but the
C<speak> method from the CPAN version of Frog, and all static calls to Frog
methods would actually be made against the new Japanese version.

It has been suggested that this RFC attempts to solve the problem in a less
than ideal way.  For example, Damian Conway suggested solving the problem
by either using delegation, or localizing code:

     my $forest = Forest->new();

     if (can_see_Mt_Fuji()) {
       local *Frog::speak = sub { "kerokero" };
       print $forest->make_noise();    # "kerokero"
     }

     print $forest->make_noise();            # "ribbit-ribbit"

However, these approaches both seem less than optimal.  Delegation, even if
sped up through compile time support (RFC 193), would require one to create
a new class with with to delegate functionality to the Forest class.
Localizing on the other hand requires that the programmer know and have the
ability to localize (in this case, the Frog::speak method) in every
location which would, however indirectly, call the changed method
(Forest::make_noise).

The programmer should not be required to circumvent the documented API of a
module if the module's author will not support the desired changes.
Certain changes, such as multi-lingual support for the Forest class, may
conflict with the goals of the author for said module - this does not mean
that a programmer desiring a multi-lingual Forest class should find it
necessary to understand and circumvent the internals of the Forest class
and all supporting classes (Frog).  Perl should provide a more elegant
solution to this problem than localizing (which avoids the documented API
and will suffer problems if the internals of the module change) or
delegation (which requires at the very least additional code written which
goes against Perl's philosophy of laziness).

Another example of where this feature would be helpful is when a large
number of classes inherit from a single superclass and it is desirable to
extend said superclass without making modifications to any subclasses.
This has tremendously powerful implications in the area of data inheritance
as this would allow the addition of fields to a superclass through
L<fields> (or the feature of similar functionality in perl6) thus affecting
all subclasses of that superclass.

Therefore, it is proposed that a method of defining collections of classes
be implemented.  A collection would encompass a set of classes that
inherited from and referenced one another (most likely a full application
or library).  By creating a new collection with only the classes you wish
to change (such as a superclass) and having this new collection inherit
from the base collection, classes will become able to overload other
classes.

I am unsure of what syntax this feature would employ, both in declaring
membership of a collection, and in declaring inheritance from a collection.
I can envision a pragma that might suffice:

     package Frog;
     use collection member => 'Japanese'; #declares collection membership
     use collection isa => 'BaseImplentation'; #declares collection inheritance

but am interested to hear suggestions for other ways this might be
accomplished.

I am unaware of any OO language that allows overloadable classes as
described here, and have seen them described only once, but have run into
many situations in which they would make sense.  I suggest this as a
solution to the problem described here for perl 6.

=head1 IMPLEMENTATION

Let it be said that I am not an internals person, nor do I claim to know
enough about the current or planned perl internals to comment on this
authoritatively.  However, there are two areas of concern I am aware of to
which an implementation of class collections must direct its attention:

=over 4

=item * Class Names Would No Longer Be Unique

A namespace alone would no longer reference a unique stash, as many stashes
might contain code and data for one namespace (such as 'Frog').  Therefore,
a namespace and collection name pair would be needed to specify a unique
stash.

=item * Method Lookup

Method lookups would need to examine the collection inheritance tree to
determine which class (that is, in which collection) to dispatch method
calls to.

=back

=head1 REFERENCES

Tim Sweeney, "A Critical Look at Programming Languages"
http://www.gamespy.com/legacy/articles/devweek_b.shtm




Reply via email to