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

=head1 TITLE

Objects : Hierarchical calls to initializers and destructors

=head1 VERSION

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

=head1 ABSTRACT

This RFC proposes a new special method called C<BUILD> that is 
invoked automagically whenever an object is created. Furthermore,
it proposes that both C<BUILD> and C<DESTROY> methods should
be invoked hierarchically in all base classes.

=head1 DESCRIPTION

One of the major limitations of object-oriented Perl is that, unlike
most other OO languages, it does not automatically invoke the 
initializers and destructors of base classes, when initializing
or destructing an object of a derived class.

This leads to tediously complex code in constructors and destructors 
in order to manually achieve the same effect. More often, it leads to bugs.

It is proposed that Perl 6 introduce an automatic object initialization
mechanism, analogous to the automatic object clean-up mechanism 
provided by C<DESTROY> methods. 

It is further proposed that both the initialization and destruction
mechanisms automatically call their corresponding base class versions
to ensure that complete initialization and destruction of derived
objects occurs correctly.

=head2 The C<BUILD> method

It is proposed that, if a class has a method named C<BUILD>, that method
will be invoked automatically during any call to C<bless>. It
is further proposed that C<bless> be extended to take an optional argument
list after its second argument, and that this list would be passed to
any C<BUILD> method invoked by the C<bless>.

The typical constructor would then be reduced to:

        package MyClass;
        
        sub new { bless {}, @_ }
        
with initialization handled in a separate C<BUILD> routine:

        sub BUILD {
                my ($self, @ctor_data) = @_;
                # initialization of object referred to by $self occurs here
        }
        
        
=head2 Hierarchical C<BUILD> calls

It is proposed that when an object is blessed, I<all> of the C<BUILD> methods
in any of its base classes are also called, and passed the argument list
appended to the invocation of C<bless>. C<BUILD> methods would be called
in depth-first, left-most order (i.e. ancestral C<BUILD> methods would be
called before derived ones). Any given C<BUILD> method would only be called 
once for the same object, no matter how many separate paths its class might be 
inherited through.

For example, given the following class hierarchy:

        package Base1;

        sub new { bless {}, @_ }
        sub BUILD { print "Base1::BUILD : @_\n" }


        package Base2; 

        sub BUILD { print "Base2::BUILD : @_\n" }


        package Base3; 

        sub BUILD { print "Base3::BUILD : @_\n" }


        package Derived1; 
        use base qw(Base1 Base2);

        sub BUILD { print "Derived1::BUILD : @_\n" }


        package Derived2; 
        use base qw(Base2 Base3);

        sub BUILD { print "Derived2::BUILD : @_\n" }


        package Rederived1; 
        use base qw(Derived1 Derived2);

        sub BUILD { print "Rederived1::BUILD : @_\n" }


then the call to:

        $obj = Rederived->new(1..3)

would print:

        Base1::BUILD : 1 2 3
        Base2::BUILD : 1 2 3
        Derived1::BUILD : 1 2 3
        Base3::BUILD : 1 2 3
        Derived2::BUILD : 1 2 3
        Rederived1::BUILD : 1 2 3

Note in particular that C<Base2::BUILD> is only called once (as early as
possible), even though class Rederived inherits it through two distinct paths.


=head2 Hierarchical C<DESTROY> calls

It is further proposed that when an object's destructor is invoked, all
inherited destructors would also be invoked, in depth-I<last>, right-most
order. Again, each C<DESTROY> for an object would be called exactly once, 
regardless of how many different paths it is inherited through.

For example, given the following class hierarchy (with the same topology
as the example for C<BUILD> above):

        package Base1;

        sub new { bless {}, @_ }
        sub DESTROY { print "Base1::DESTROY\n" }


        package Base2; 

        sub DESTROY { print "Base2::DESTROY\n" }


        package Base3; 

        sub DESTROY { print "Base3::DESTROY\n" }


        package Derived1; 
        use base qw(Base1 Base2);

        sub DESTROY { print "Derived1::DESTROY\n" }


        package Derived2; 
        use base qw(Base2 Base3);

        sub DESTROY { print "Derived2::DESTROY\n" }


        package Rederived1; 
        use base qw(Derived1 Derived2);

        sub DESTROY { print "Rederived1::DESTROY\n" }


then the destruction of an object:

        $obj = "something else";

would print:

        Rederived1::DESTROY
        Derived2::DESTROY
        Base3::DESTROY
        Derived1::DESTROY
        Base2::DESTROY
        Base1::DESTROY

Note that C<Base2::DESTROY> is only called once (as late as possible),
even though class Rederived inherits it through two distinct paths.

Note too that the C<DESTROY> invocation sequence is the exact reverse
of the sequence for C<BUILD>.

=head3 The C<REBUILD> method

When a blessed object is reblessed into a different class, the C<REBUILD>
methods of the I<new> class (and its ancestors) would be called, rather than
it's C<BUILD> methods. The C<REBUILD>s would be called in the
same "ancestor-first" sequence as for C<BUILD>.

As well as any arguments passed to the C<bless>, C<REBUILD> would also be
passed -- as its second argument -- the name of the previous package,
so it could choose to invoke any cleanup methods from that package if
it so desired.  For example:

        # Full "destroy/create transfer" on reblessing...

        sub REBUILD {
                my ($self, $oldclass, @blessargs) = @_;
                $cleanup = "${oldclass}::DESTROY";
                $self->$cleanup();
                $self->BUILD(@blessargs);
        }

versus:

        # Simple "clear private data" on reblessing

        sub REBUILD {
                my ($self, $oldclass, @blessargs) = @_;
                @{$self}{grep /^_/ keys @$self} = ();
        }


=head2 Proposed standard C<UNIVERSAL::new>

Given that most Perl classes are hash-based and that the C<BUILD> method
cleanly separate construction and initialization, it might be desirable to 
have the UNIVERSAL class always supply a default constructor:

        package UNIVERSAL;
        
        sub new { bless {}, @_ }
        
If this were provided, many classes could simply define an appropriate
C<BUILD> method and simply inherit their constructor.
        

=head1 MIGRATION ISSUES

Changes to the behaviour of DESTROY might have serious impact on code
that overrides destructors in derived classes, especially if the overridden
destructor delegates explicitly to its base class counterpart.


=head1 IMPLEMENTATION

Not difficult.

=head1 REFERENCES

I<perltoot>

Conway, I<Object Oriented Perl>, pp. 171-178.

Reply via email to