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 Sep 2000 Last Modified: 18 Sep 2000 Mailing List: [EMAIL PROTECTED] Number: 189 Version: 3 Status: Frozen =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. =head2 Alternative naming schemes During discussions on this RFC, some people indicated a preference for C<BLESS>/C<REBLESS> or C<NEW>/C<RENEW>, rather than C<BUILD>/C<REBUILD>. =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.