April 11, 2003

Parrot Objects, from the outside

I've been struggling with objects for Parrot for quite a while, as many people on p6i will attest to. I think I've sort of got it, but since writing down a mild draft of what I'm thinking about often helps (and isn't really suitable for a PDD) I figure I'll dump out here, and see how things go. This is also useful for folks looking at how we're trying to do OO stuff.

The first important thing to realize is that up until recently I didn't do much OO work, at least not "real" OO. Sure, data encapsulation and indirect function pointers and vtables and such, but all very explicit, so this is something of a new(ish) thing. That and my first intro to OO was ages ago with C++, and then to Object Cobol. I still bear the scars.

Parrot's also in an unusual position where it has two major OO systems it needs to deal with--the perl 5 style "anything goes, go for it, good luck, mind the bear traps" of objects, and what I'm told is a more traditional object system, along the lines of Java/C++/Ruby/Python. And each type needs to inherit from the other. With multiple inheritance. And multimethod/signature based dispatching. Oh, and lets not forget interfaces! At least on the back side I can count on their being classes, which is something.

Needless to say, this is something of a challenge. So, to help me deal with it, I've tried to partition it into pieces, so I can deal with each piece in turn. The pieces are:

  1. Code which uses objects
  2. Code in classes
  3. Inheriting from a non-Parrot Standard Class
  4. Modifying the attribute list of a standard class

There are probably more, and I expect I'll add to the list. Heck, that'll help partition things, which is good.

Anyway, this time around I want to talk about the first point, using objects. This is for code which treats objects as opaque things. There's no knowledge of the internals of the object--it's a thingie in its own right. (Yes, I know, some systems let, or even encourage, you to peek inside objects. Lets not go there at the moment, that's a different class of code from what I'm talking about)

User code needs to be able to call methods on objects. They need to get and set properties (which isn't, strictly speaking, an object thing, but...), get the property hash, get a PMC for a method for later calling, and methods have to override properties of the same name, so if you get a property by name and there's a method of that name, you get the result of that method being called with no args. Well, it can, but it doesn't have to, strictly speaking. (Some languages may decide not to do this) Being able to get the class identifier for an object's a darned useful thing as well. We also need to see if an object is a member of a class, implements an interface, or has a method of a particular name. (And yes, we could fake up the name lookup with a fetch of the method PMC and check for failure, but we're not going tt)

This list, luckily, is reasonably short. Parrot satisfies, or will satisfy, all of these requirements though PMC vtable entries. (Which also means that any PMC could, potentially, act as an object. Which is kind of cool, when you think of it, if you're really fond of objects. Or not at all fond of objects but hanging around with people who are fond of them)

To do this, Parrot needs the following vtable entries:


  1. Get Property
  2. Set Property
  3. Get Property Hash
  4. Call Method
  5. Get Method PMC
  6. Get Class ID
  7. See if an object is from a class or a child of a class
  8. See if an object implements an interface
  9. See if an object has a method of some particular name

Plus versions that take keys, in case people do things like @foo[12].bar(). I don't expect that'll be too common, though I do think code that looks like %commands{$command}.run(@params) will be, if for no other reason than that's the sort of thing that I tend to do.

That, as they say, is that. No knowledge of the internals of anything's needed.

Pity it's not quite enough to actually implement anything, since without a standard class system it's a bit fuzzy. I think that's it, though.

Posted by Dan at April 11, 2003 05:55 PM | TrackBack (1)
Comments

I'll be the first to admit that I have no clue about Parrot, and only a small clue about Perl 6, and only a tiny clue about implementing object systems, but I thought that this might be useful:

This discussion reminds me a bit of Objective-C's object system. Objective-C is interesting because it is implemented as C library. This makes it possible to "hack" objects together at runtime, including inheritance, method calls and all that fun stuff. This isn't hugely useful for Objective-C, but it probably would be when trying to support the Perl5 object model. Apple has a document called Objective-C Runtime Functions and Data Structures which sheds some light on the Objective-C internals. Another good resource is GNUStep's implementation.

Anyway, ObjectiveC's object library contains the following functions, listed with their rough equivalents from the list above:

object_setInstanceVariable - Set Property.
object_getInstanceVariable - Get Property.
objc_msgSend - Call Method.
class_getInstanceMethod - Get Method PMC plus "See if an object has a method of some particular name".
objc_getClass - Get Class ID.

The remaining two (see if an object is an instance of a class or a child of a class, and see if an object implements an interface) are implemented by the root object (NSObject) in terms of the primitives above (it examines the low level Objective-C class structures returned by objc_getClass).

So I guess what I'm trying to say, is that I think you are correct: You have a good list of primatives needed to implement an object system, since essentially that is what Objective-C uses.

Posted by: Evan Jones at April 11, 2003 07:28 PM

Ah, Objective-C. I'd forgotten about that for a bit, which is silly as I run OS X as my primary desktop system and fiddle quite a lot with the perl Cocoa bridge. D'oh!

Now, on to classes and more... interesting things.

Posted by: Dan at April 11, 2003 08:58 PM

You might be interested to see how PHP5 (Zend Engine 2) does this. Its come with a really nice, spanking new object model (well, it would be really nice if we had a decent VM arch.)

Anyhow, These files will give you a decent idea of both the features and the design:

http://cvs.php.net/co.php/ZendEngine2/OBJECTS2_HOWTO?login=2&r=1.5
http://cvs.php.net/co.php/ZendEngine2/ZEND_CHANGES?login=2&r=1.56

Its really nice for overloading, and calling out to external sources (I was able to integrate mono/CLR in a weekend, knowing neither mono or Zend 2).

Posted by: Sterling Hughes at April 12, 2003 10:49 AM

No decent VM arch? Hrm, have I mentioned my Evil Master Plan to you? :)

Unfortunately it's the VM end of things I need to deal with, since I don't have to specify the user semantics of objects. That's someone else's job, and one that's been pretty much done (and done to death) by other folks. At some point Larry needs to write the Object Apocalypse for perl 6, but I've a pretty good idea what he wants, what perl 5 does, and what Ruby does, plus a middling idea of how Python works. I've not looked at PHP yet, but I probably should.

Posted by: Dan at April 13, 2003 09:37 AM

Parrot is something i'm looking into, if only for the fun factor. It shouldn't be that hard to do a PHP compiler for parrot - I can try and whip one up today actually.

The main problem will be lack of all the chewy goodness that comes from the non-engine portions of PHP. Auto-propagation of $_GET, $_POST, etc. variables. Interaction with multiple server SAPIs. The host of builtin functions written for PHP. I wish we had actually done that interlanguage layer discussed after OSCON awhile ago, would've made parrot much easier (yes, I know swig, but I'm not happy with it. :)

Btw, I was more refering to PHP's object VM architecture/api, then the user-level api. The user-level stuff will make you laugh (or cry), as its become terribly bloated (interfaces and abstract classes are verified at runtime, import overrides the global scope for "performance," etc.)

Posted by: Sterling Hughes at April 13, 2003 11:00 AM

The environment for parrot is one of the things on my darned huge list 'o things to do. It's actually pretty simple with the right generated glue code (you can use a special PMC for getting and setting environment variables, for example) though alternate APIs for embedders is somewhat problematic--making Parrot plug-compatible at the C API level's not on the list 'o stuff. I'm all for it if we can manage, but if not I'm fine with that. One or two major things at a time and all.

I should go dig into the PHP object model under the hood, though as I should post here in a while, the constraits Parrot has are... interesting. Supporting multiple models with proper cross-model inheritance, delegation, and re-issuing of method calls is a bit of a pain, as you might imagine. :)

Posted by: Dan at April 14, 2003 12:14 PM

Seems your blog doesn't like trackbacks, anyhow: http://www.edwardbear.org/blog/archives/000156.html

I've got the two partially integrated, but translating between the two is turning into a big hack (i don't know how to efficiently map the systems).

Posted by: Sterling Hughes at April 15, 2003 11:19 AM

Huh, weird. The trackback notification got mailed, and it seems to be showing, but I may have messed up the templates so the numbers aren't everywhere. I'll go see what I can do to fix.

But...

What you've done is really nifty. It's not actually necessary to map from a stack to register system, though it's definitely more efficient if you can. I keep meaning to put together a set of stack ops to support stack-based systems. Mostly for the first draft of the python bytecode converter (well, OK, I'll probably use it for the z-machine code too) but they ought to be of general use, at least once I write the darned things. I'll go read your blog entry and see what we can do. :)

You might want to take a look at IMCC, in the languages/imcc subdir, as it takes care of register remapping and some of the other annoying/nasty parts of dealing with register machines, such as register spilling and suchlike stuff.

Posted by: Dan at April 15, 2003 11:44 AM

*Ohh* Imcc is definitely goodness. I'll work on modifying my compiler to generate imcc instead of parrot, time permitting. :)

Posted by: Sterling Hughes at April 15, 2003 12:21 PM