This and other RFCs are available on the web at http://dev.perl.org/rfc/ =head1 TITLE Extensible Meta-Object Protocol =head1 VERSION Maintainer: Tony Olekshy <[EMAIL PROTECTED]> Date: 11 Aug 2000 Last Modified: 1 Oct 2000 Mailing List: [EMAIL PROTECTED] Number: 92 Version: 2 Status: Frozen =head1 ABSTRACT In the name of keeping Perl Perl and easy easy, Perl 6 should maintain the current syntax and semantics for blessed objects. Perl 6 should not I<require> the use of an alternative meta-object protocol, nor should it I<prevent> the development of other protocols (such as those which implement a stricter protocol). However, Perl 6 should provide hooks into object protocol mechanisms that would enhance the development of modules that implement other meta-object protocols. This RFC considers the matter of an extensible method search protocol, which allows things like limited visibility ivars and methods, and breadth-first multiple inheritance, to be efficiently implemented as modules rather than as core Perl behaviour that would be forced on all. =head1 DESCRIPTION Perl's current object-oriented stuff works well for certain classes of problems. It doesn't interfere with Perl programs that don't want to be object-oriented at all. It provides a foundation upon which more complex object protocols can be implemented. However, since all method names are visible in all contexts (they are effectively "public"), it is difficult to write frameworks containing abstract base classes that are designed to be heavily inherited from, because internal ("private") methods can't be added to the base classes without impacting all users of the framework. Similar problems arise with instance variables. At Avra we address these problems by using a module that allows us to write publically visible framework base classes like this: package MyClass; use Prothos::Class ISA => ParentClass; ivar Foo => Public, Write => Private; ivar Bar => Private, Default => "Hello, World"; ivar Baz => Protected, Default => {}; method HelloWorld => Public, sub { my ($I, %A) = @_; $I->Baz(Name => $A{Name}); print $I->Baz("Name"), " says \"", $I->Bar, ",\" to $A{To}.\n"; }; method Cogitate => Private, sub { ... }; method OverrideMe => Protected, Bind => Virtual, sub { ... }; These classes play well with canonical Perl classes. Only the behaviour of the package is affected, Perl is not globally affected. Our module constructs objects as blessed references to an integer which is an index into a hidden generator-scoped array of all extant objects in the class, which means there is no way to get to an actual object's data unless one of the (generated) methods that can see the hidden array is used. The ivar declarations are converted into generated method subs that access the hidden ivar data. Ivars are internally qualified by package name, so shadowed ivars are correctly handled. Methods' subs are I<saved> in a hidden internal hash, and generated subs are installed in the package namespace to fetch and invoke the correct method closure from the hidden internal hash, using rules that depend on the declared visiblity of the method. This works because all the generated subs of the same name are I<the same>, independent of package, so no matter which one is found by Perl, the caller() context and visibility rules are used to determine the actual closure to invoke. The generated methods basically look like this (where $_class and $method are the names of the class and method): $_stash->{$method} = \&{"${_class}::$method"} = sub { $_ = $_cache->{$method}->{scalar caller} and goto $_; goto \&{ &{$_stash->{_First_}}($method, @_) }; }; The run-time cost of setting all this up and doing the cache initializations and lookups is acceptable to us. However, our I<method invocations now take up to twice as long>, even with caching, because there are two "function calls", one for the C<sub> and one for the C<goto>. This problem can be solved by allowing Perl's method search mechanism to be overridden by an installed subroutine, on a per-package basis. =head1 IMPLEMENTATION When Perl performs a method lookup it starts in the package of the given object and searches up the tree of @ISA packages. Perl should be modified so that if C<$ISA::Search> (or equivalent) is defined for a package, then when a method is being looked up for an object in that package, the code ref given by that value is invoked instead of performing the default search. The code ref should be passed the class name, method name, and the value of C<scalar caller> (since method visibility algorithms typically depend on the name of the invoking context). The code ref should be written to return the package-qualified sub name to be invoked. This return value should be memoized by Perl, that is, given the same class name, method name, and C<scalar caller>, the search code ref should not be invoked at all. Asymptotically, this gets us back down to one "function call" per method invocation. In general, it should be possible to override the ISA search mechanism, the -> method invoker, and the C<bless> operator, on a per-class basis. =head1 TERMINOLOGY A "meta-object protocol" is the set of rules that determines the behaviour of objects. For more information see section 1.6 in the OO FAQ at http://www.cyberdyne-object-sys.com/oofaq2 , from which: "In CLOS terminology, an introspective protocol provides a read only capability (e.g. what is this object's class, give info on this class, etc.) and an intercessory protocol provides a write capability which allows system modification (e.g. add the following method or instance to this class, perform inheritance this way, etc.). Because inheritance can be used to perform differential changes, intercessory protocols allow users to not only define new frameworks but to specialize existing system frameworks differentially without affecting them and their extant objects. Thus, many frameworks can interoperate together simultaneously." =head1 REFERENCES RFC 28: Perl should stay Perl. OO FAQ: http://www.cyberdyne-object-sys.com/oofaq2