Team LiB   Previous Section   Next Section

5.5 Objects

Perl 5, Perl 6, Python, and Ruby are all object-oriented languages in some form or other, so Parrot has to have core support for objects and classes. Unfortunately, all these languages have somewhat different object systems, which made the design of Parrot's object system somewhat tricky.[8] It turns out that if you draw the abstraction lines in the right places, support for the different systems is easily possible. This is especially true if you provide core support for things like method dispatch that the different object systems can use and override.

[8] As I write this it's still in progress, though it should be done by the time this book is in print.

5.5.1 Generic Object Interfacing

Parrot's object system is very simple—in fact, a PMC only has to handle method calls to be considered an object. Just handling methods covers well over 90% of the object functionality that most programs use, since the vast majority of object access is via method calls. This means that user code that does the following:

object = some_constructor(1, 2, "foo");  
object.bar(12);

will work just fine, no matter what language the class that backs object is written in, if object even has a class backing it. It could be Perl 5, Perl 6, Python, Ruby, or even Java, C#, or Common Lisp; it doesn't matter.

Objects may override other functionality as well. For example, Python objects use the basic PMC property mechanism to implement object attributes. Both Python and Perl 6 mandate that methods and properties share the same namespace, with methods overriding properties of the same name.

5.5.2 Parrot Objects

When we refer to Parrot objects we're really talking about Parrot's default base object type. Any PMC type that implements the method call vtable entry is an object as far as Parrot is concerned, but while that's sufficient to use an object, it's not enough to make the objects actually work.

Parrot's standard object uses a slot-based attribute model. Each object is essentially a small array, with one element per attribute in the object's class and superclasses. Each object carries a directory of which slots are used by which classes for which attributes. This allows introspective data browsers to show objects at runtime and runtime additions of attributes to objects.

Parrot's standard object and its base class aren't particularly special, and certainly not the optimal universal object type. Its characteristics were chosen to meet the needs of the Perl 6 object system, though it should suffice as a base type for Ruby, Python, Java, and .NET objects.

5.5.3 Parrot Classes

As with objects, Parrot requires some base functionality of classes. Unlike objects, which have a required action (the method call), classes have required metadata. Since Parrot classes are themselves PMCs, the required metadata is stored as properties on the class PMCs. Classes may also reserve a section of the namespace and define subs and methods in that namespace, but that's not strictly required.

To conform to Parrot's standards, a class must expose a class type property. This property specifies whether the class is a subclass of Parrot's base object class, a class that isn't a subclass of Parrot's base class but is still available for inheritance, or an opaque class that can't be subclassed by Parrot's base class.[9]

[9] Remember, Parrot supports multiple inheritance.

Opaque classes are easy. We just ignore them and assume that their internal code (or code emitted by the languages that use them) know how to manipulate, subclass, and otherwise fiddle with them. Generic Parrot code inspectors won't be able to dive into them, but that's not necessarily a bad thing. You wouldn't expect to look at the source for a C++ class from within Parrot.

Parrot's base class is also simple enough to handle. Since we have full control over it, Parrot can peek and cheat as needed. The base class, as you've seen, is based on an attribute array model and comes with a set of default methods for method dispatch, property fetching, etc.

Inheritable classes that aren't based on Parrot's base class are another matter entirely. Parrot supports them with transparent delegation, as we'll see in the next section. The classes themselves don't have to do much to get this support. A non-base inheritable class must set properties on the class object that tell Parrot where to find its allocation and initialization methods. It also must make sure on method dispatch that the object isn't really a delegated part of a containing object. The engine provides code to do this efficiently.

5.5.4 Mixed Class-Type Support

The final piece of Parrot's object system is the support for inheriting from classes of different types. This could be a Perl 6 class inheriting from a Perl 5 class, or a Ruby class inheriting from a .NET class. It could even involve inheriting from a fully compiled language such as C++ or Objective C, if proper wrapping is established.[10]

[10] Objective C is particularly simple, as it has a fully introspective class system that allows for runtime class creation. Inheritance can go both ways between it and Parrot.

As we talked about earlier, as long as a class either descends from the base Parrot class or has a small number of required properties, Parrot can subclass it. This potentially goes both ways, as any class system that knows how to subclass from Parrot's base class can inherit from it.

Allowing classes to inherit from other classes of a different base type does present some interesting technical issues. The inheritance isn't 100% invisible, though you have to head off into the corner cases to find the cracks. It's an important feature to design into Parrot, so we can subclass Perl 5 style classes, and they can subclass Parrot classes. Being able to subclass C++ and Objective C classes is a potential bonus. Python, Ruby, and Perl 6 all share a common (but hidden) base class in Parrot's base object type, so they can inherit from each other without difficulty.

    Team LiB   Previous Section   Next Section