Right, so with at least a basic rework of the string stuff in, it's time to turn our attention to objects and all the stuff that goes with them.

I'd originally thought that the bits we'd put in place would be sufficient to do everyone's object system (well, all the languages that we explicitly care about at least) but, well... that turns out not to be the case.

Here's a quick recap on the architecture bits that already exist.

Each PMC can:

   *) Return a PMC which represents that PMC's class
   *) Look up and return a PMC representing a named method
   *) Look up and return a named property for the PMC

I think that we can accommodate most everyone's needs with this, assuming we set up some protocols and give a really big kick to the code in objects.c.

The rest of this should be prefaced with the caveat that I'm finding that the more I'm learning about how all the different languages do objects the less I actually know what's going on, so this all might be wrong, or at least really sub-optimal.

Anyway, with the vtable slots as they stand, we can make a method call on an object. There's a single op for this -- it calls the object's find_method function and then invokes the resulting PMC. The object is in complete control over what invokable PMC gets returned, and the invoked PMC has full control over what it actually does.

Python has this interesting concept where properties and methods are sort of mixed -- that is if you do:

    a = foo.some_thing

a will be either the property some_thing or the method object you'd get if you invoked some_thing on foo, bound up such that you can later do:

    a()

and it's the same as if you'd done foo.some_thing(). I think. Pretty sure, at least, though I'm not sure of the order. Anyway, for this the core functionality is fine, though it assumes that the python compiler will emit a "fetch prop/test/fetch method/bind method" sequence for the a=foo.bar statement. I think I'm OK with that, though I'm more than willing to take arguments to the contrary. ("It's not easy" isn't a good argument -- we're down at the machine level. Nothing's easy. "It breaks because of X and therefore has to be a fundamental thing to guarantee it works" is a good argument)

This assumes we have binding functionality that takes some parameters and produces an invokable PMC that uses those, and any other provided, parameters later on. Given that Perl 6 is doing higher-order functions (or curried functions, or whatever) where there's going to be a fair amount of binding going on I'm OK with core support for this, where core may be "we have a bind PMC type with some methods on it" or something like that. Dunno, we can work that out later.

PMCs also implement 'is', 'does', and 'can'. These are three vtable methods that allow code to query a PMC to see if it it 'is' an instance of a named class, if it 'does' a particular interface, and whether it 'can' invoke the named method. PMCs are supposed to answer truthfully, however no assumptions are made as to how the pmc actually is, does, or can do something. It's perfectly acceptable for a PMC that has an AUTOLOAD (or its non-perl equivalent) function to answer true to any can query, and how an interface is implemented in a PMC's parent classes (if indeed it even has any parent classes) is also generally irrelevant, so long as the answers are either correct or egregiously wrong.

Anyway, so much for the 'outside world' view of objects as black box things that have properties and methods. That part's easy, and I think we're fine there.

The tricky part is all the bits that back the objects. Or, rather, arranging so that everyone can reasonably have their own custom backing bits while still allowing mix-n-match inheritance between the bits, and providing enough information to do things properly, or to cleanly fail. We're not going to *require* that classes of different sorts be able to inherit from one another, just require that if they do allow that then they behave properly.

Almost everything we do here is going to be with method calls. There's very little that I can see that requires any faster access than that, so as long as we have a proper protocol that everyone can conform to, we should be OK.

The one big exception to the "we use methods" thing is with composite objects. The only sane way I can think of to handle cross-system inheritance is through delegation and composition -- that is, if you have class A which inherits from Foo and A_Class, each of which is from a unique object system (like, say, CLOS, perl 5's 'object system', and new-style python classes), there's no way a single class system can reasonably express the differing semantics, so the only sane thing to do is have a composite object. The 'main' object is class A's (however that class instantiates objects) and hanging off it are two sub-objects, one for Foo and one for A_Class. These sub-objects will have a flag set marking them as a sub-object, and will have the master composing object as a property. We can get into how this is handled later. The important thing here is that we are going to have a flag bit that says "I'm only part of an object" and whenever a method call is made on a sub object parrot will automatically look for the 'master' object and make the method call on it instead.

Parrot's object system is going to be generally class-based -- that is, each object has a class that's responsible for managing the object. Classes, being objects, themselves have classes, but past that we don't look too closely or we'll end up getting bitten by the snake. (No, not that snake, the other one) This does mean that prototype-based object systems are going to be kinda annoying, but if you do things right then the object you're using as a prototype is your class anyway, so it ought to work out OK. I think. Hopefully, at least, since prototype-based object systems aren't our main interest.

A class is responsible for instantiating objects, providing basic information, providing subclasses of itself, merging in with another class for multiple inheritance, and managing methods. Basically we access the class object when we want to subclass a class, add a class into another class' inheritance hierarchy, make new objects, and mange a namespace. (I think. we might go with classes managing their own methods, or may just declare that classes are going to find their methods in a namepsace of the same name so had darned well better go look there. I'm as yet somewhat undecided -- selector namespaces are awfully tempting, and there's MMD to consider)

So. What does a class need to be able to do? I'm thinking the following basic methods (the names are up in the air)

    subclass - To create a subclass of a class object
    add_parent - To add a parent to the class this is invoked on
    become_parent - Called on the class passed as a parameter to add_parent
    class_type - returns some unique ID or other so all classes in one
                 class family have the same ID
    instantiate - Called to create an object of the class
    add_method - called to add a method to the class
    remove_method - called to remove a method from a class
    namespace_name - returns the name of this class' namespace

All objects also must be able to perform the method:

    get_anonymous_subclass - to put the object into a singleton anonymous
                             subclass

I can see adding some information-fetching methods for introspection to this list, so I'm up for those too.

Some fallout you might not have thought about: Objects are ultimately responsible for handling method finding, which means that each object controls the search order of its parent classes when looking for methods, so if you want to fiddle with that, it's fine. It also means you can mess around with the class hierarchy as it appears to external code (fibbing about who you are, or aren't) and suchlike things.

Now, there is one big gotcha here -- multimethod dispatch. I'm not entirely sure that we can do this properly and still give classes full control over how methods are looked for and where they go. I *think* it can be done, but I'm not feeling clever enough to see how without having a relatively costly double lookup on every method call (first to see if there's a registered MMD method for the named method, then the regular dispatch if there's not) so I'm not sure we will. Efficient MMD wins, if we can make it look like perl/python/ruby/tcl's method lookup rules are in force, even if they really aren't under the hood.
--
Dan


--------------------------------------it's like this-------------------
Dan Sugalski                          even samurai
[EMAIL PROTECTED]                         have teddy bears and even
                                      teddy bears get drunk

Reply via email to