So, if I understand this right (and I may well not), when you instantiate a metaclass you get a class, and when you instantiate a class you get an object, and since anything you instantiate is an object anyway that means classes are objects. I'm not entirely sure if metaclasses are objects, but it seems to lack a certain amount of egregious symmetry if it doesn't.
That means, I think, that you ought to be able to call methods on classes, which since you can I think we're mostly OK, though I expect some reworking of base behaviour might be in order. (Though nobody outside of us on the list are likely to ever use it so far as I can tell :)
The one question I have is whether we need to have a "call class method" operation that, when you invoke it, looks up the class of the object and redispatches to it, or whether class methods are just methods called on class objects. -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
Dan Sugalski wrote: > So, if I understand this right (and I may well not), when you > instantiate a metaclass you get a class, and when you instantiate a > class you get an object, and since anything you instantiate is an > object anyway that means classes are objects. I'm not entirely sure if > metaclasses are objects, but it seems to lack a certain amount of > egregious symmetry if it doesn't.
metaclasses are objects as well.
> That means, I think, that you ought to be able to call methods on > classes, which since you can I think we're mostly OK, though I expect > some reworking of base behaviour might be in order. (Though nobody > outside of us on the list are likely to ever use it so far as I can > tell :)
> The one question I have is whether we need to have a "call class > method" operation that, when you invoke it, looks up the class of the > object and redispatches to it, or whether class methods are just > methods called on class objects.
With Ruby class methods are just method calls on the class objects, so there's no for a "call class method" operation.
Languages like Python might need one though but I can't say for certain as my knowledge of Python is very sketchy.
One difficulty is when calling class methods some languages require that you provide the class object as the receiver (Ruby and c# do this) while some languages let you use an instance of the class as the receiver (Java does this)
so in Ruby foo = Foo.new foo.class_method will produce an error
but in Java foo = new Foo foo.class_method will work
Though rather than having a seperate call class method operation it might be better to hide the logic that checks for any class methods withing the JavaObject pmc.
Dan Sugalski <d...@sidhe.org> wrote: > The one question I have is whether we need to have a "call class > method" operation that, when you invoke it, looks up the class of the > object and redispatches to it, or whether class methods are just > methods called on class objects.
The terms are misleading a bit here. - a ParrotClass isa delegate PMC - a ParrotObject isa ParrotClass - a HLL class isa Parrotclass and a low-level PMC object - a HLL object isa all of above
.sub _main .local pmc class .local pmc obj newclass class, "Foo" # create a new Foo class find_type $I0, "Foo" # find it's dynamic type number new obj, $I0 # instantiate a foo object obj."_meth"() # call obj."_meth" class."_meth"() # and as a class method end .end
> One difficulty is when calling class methods some languages require that > you provide the class object as the receiver (Ruby and c# do this) while > some languages let you use an instance of the class as the receiver > (Java does this)
I think you're confusing what it looks like and what it does. When designing a VM such as parrot it's very important to keep the two things separate. "What it looks like" is the realm of the compiler. The compiler maps from the language syntax to the execution capabilities provided by the virtual machine and runtime. Even if from the syntax point of view it looks like static methods in C# require the class name to invoke them, this doesn't mean a 'class object' is involved at all. Same with java: if it's allowed to call a static method with the syntax: instance_of_class.static_method_of_class (); it doesn't mean instance_of_class is involved in the call: it isn't. The compiler will find static_method_of_class and emit a direct call to it, possibly discarding instance_of_class completely (unless it's an expression with possible side effects, but in any case, the instance object is not involved in the call).
"What it does" is the realm of the virtual machine or runtime. The virtual machine needs to provide enough power for the compiler to be able to implement its specific languuage semantics (either directly or through helper methods/libraries). Of course, if the VM provides more than one way to do it, the compiler should try to use the more efficient one. For example, on the CLR you can call a static method in at least three ways: * directly from IL code (same overhead as a direct C function call) * using reflection (provides lots of flexibility, since it can be done at runtime, arguments are converted to the correct types if needed etc. Of course the price to pay is slowness...) * delegate invocation: some of the benefits of a reflection call with an overhead just slightly bigger than a direct call
I think parrot should provide two mechanisms: a fast one for languages that can take advantage of it and a more dynamic one for use by the other implementations. Of course the main issue becomes: what happens when two different langauges with different semantics need to call each others's methods? This is the main issue that parrot faces if it is to become a real VM for dynamic languages, but sadly this problem space has not been addressed yet, mostly because of the lack of real langauge implementations (but the pie contest is rapidly approaching and it's likely to be a big driving force:-) The PHP compiler is also progressing nicely from what I hear, so it's likely that by summer some people will have to actually deal with inter-languages calls).
Some of the call issues are present already: I don't remember if it has been addressed somewhere already, but how does parrot deal with perl (arguments passed in the @_ array) with the parrot call convention that passes some args in registers and some not? For example, consider: sub my_func { my ($arg1) = shift; do_something (@_); } When it is called with: my_func (1); my_func (1, 2, 3, 4, ..., 11, 12, 13); my_func (@an_array); Since the call has no prototype some args go in registers and some in the overflow array. In the first case my_func will need to build the @_ array from the first PMC arg register. In the second case the compiler needs to create a temporary array and put some args in registers and some in the overflow temp array. When called, my_func needs to gather all the args and put them in the @_ array. In the third case the compiler needs to discard the passed array, put some of the elements in registers and some in a newly created temp array (the overflow array) unless it can play tricks by modifying @an_array (but this is only safe when @an_array is a temporary list, such as one returned from sort etc.). Again, at the method prolog, my_func needs to reconstruct the argument array @_ from args in registers and in the overflow array. Some of the shuffling could be avoided if @_ becomes a magic array which keeps some data in registers and some in the overflow array, but this has many issues itself, since the first arguments are in registers which could be overwritten at the first call. So it looks like there is already a lot of complexity and memory shuffling: has anyone generated the code (maybe by hand) to implement in parrot something like the above scenario and measured the speed characteristics vs the equivalent perl5/python code (I don't know, though, if in python the call semantics are similar to the perl5 ones...)? Thanks.
lupus
-- ----------------------------------------------------------------- lu...@debian.org debian/rules lu...@ximian.com Monkeys do it better
Leopold Toetsch <l...@toetsch.at> writes: > Dan Sugalski <d...@sidhe.org> wrote:
>> The one question I have is whether we need to have a "call class >> method" operation that, when you invoke it, looks up the class of the >> object and redispatches to it, or whether class methods are just >> methods called on class objects.
> The terms are misleading a bit here. > - a ParrotClass isa delegate PMC > - a ParrotObject isa ParrotClass
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ That definitely seems to be the wrong way 'round.
Piers Cawley <pdcaw...@bofh.org.uk> wrote: > Leopold Toetsch <l...@toetsch.at> writes: >> Dan Sugalski <d...@sidhe.org> wrote:
>>> The one question I have is whether we need to have a "call class >>> method" operation that, when you invoke it, looks up the class of the >>> object and redispatches to it, or whether class methods are just >>> methods called on class objects.
>> The terms are misleading a bit here. >> - a ParrotClass isa delegate PMC >> - a ParrotObject isa ParrotClass > ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ > That definitely seems to be the wrong way 'round.
Why? A ParrotClass is responsible for the method dispatch. The ParrotObject inherits that behavior.
In the beginning, there is MMD. Ideally as a lexically and dynamically scoped first-class variable. Dispatching on argument types, value predicates, call site, current continuation, and all sorts of other useful things.
The usual boring thing to do then is implicitly or explicitly call a dispatch function attached to the left-most participant.
In an aggressive prototype-based system, you're done. The object has defined it's own dispatcher, or got one from a parent, perhaps the root object.
In aristocratic systems, often an implicit dispatcher hands off the request to a please-dispatch-for-peon dispatcher in an associated classy noble. In systems where classy nobles are objects too, and thus have their own dispatcher, their dispatcher may be the same as please-dispatch-for-peon, or be completely different, or overlap. The classy object may feel the need to pass some combination of dispatcher and please-dispatch-for-peon requests up to a noble of its own. This hierarchy may be defined to taper off, or may appear infinite.
The relationship of peons and nobles, and their capabilities, varies. Nobles can create new peons. Peons may or may not be able to create other peons. Nobles may or may not be able to create other nobles of similar stature -- they may have to appeal to a more noble noble to do it. It may or may not be possible to grant or take away nobility. (ObjectFactory-ness).
Nobles may or may not be able to change. Either in fields, or methods, or both. If they can, this may or may not affect already created peons. Parts of their identity (name, pointer, uid, etc) may or may not change too. A peon may or may not be able to change without affecting it's noble. Same for a noble.
And things can get... odd. For example, an object may accept requests to add a field, but then have this result in creating an new singleton class which inherits from the objects current parent, and then in visibly reparenting the object... or if the parent is already a singleton, in the singleton's silent mutation. And sometimes "singleton" classes can be asked to create additional objects. Weeee.
The existing ops provide a vocabulary for describing solutions in this space. They may even be complete. So the question is how to clump and C-code them for convenient implementation of the languages we care most about.
I've got to go. A table of design-question vs P5/P6/Py/Rb might help. Two often useful concepts I don't recall noticing in the discussion are that of Root objects distinct from Object and Class, and of mixins.
>>>> The one question I have is whether we need to have a "call class >>>> method" operation that, when you invoke it, looks up the class of the >>>> object and redispatches to it, or whether class methods are just >>>> methods called on class objects.
>>> The terms are misleading a bit here. >>> - a ParrotClass isa delegate PMC >>> - a ParrotObject isa ParrotClass >> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >> That definitely seems to be the wrong way 'round.
> Why? A ParrotClass is responsible for the method dispatch. The ParrotObject > inherits that behavior.
But logically, a Class is an Object, and an object is an *instance* of a class. Surely a class should be responsible for storing and finding method, but some other thing, call it a Dispatcher object (in Smalltalk it's the Interpreter, but we've got one of those already), is responsible for the actual dispatch. By making the dispatcher drive the dispatch sequence you can do nice things like decoupling the method cache from the class itself, just have the dispatcher maintain its own cache. Then when something changes that might invalidate the cache you just tell the dispatcher to flush its cache and carry on; no need to go finding every affected class and having them flush their caches. Having a dispatcher object helps with multimethod dispatch too of course (and where are you going to stick your multimethod lookup cache if you *don't* have a dispatcher object).
Of course, if you have OO languages that have weird dispatch rules, you might need to have multiple dispatchers hanging around but (I'd argue) you're still better attaching them to classes using composition rather than inheritance.
Piers Cawley <pdcaw...@bofh.org.uk> wrote: > Leopold Toetsch <l...@toetsch.at> writes: >> Piers Cawley <pdcaw...@bofh.org.uk> wrote: >>> Leopold Toetsch <l...@toetsch.at> writes:
>>>> The terms are misleading a bit here. >>>> - a ParrotClass isa delegate PMC >>>> - a ParrotObject isa ParrotClass >>> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ >>> That definitely seems to be the wrong way 'round.
>> Why? A ParrotClass is responsible for the method dispatch. The ParrotObject >> inherits that behavior. > But logically, a Class is an Object, and an object is an *instance* of a > class.
Sure. The HLL class C<isa> ParrotClass PMC object. The HLL object is the instantiation of it. Please note that C<ParrotObject> is the underlying PMC, which can't be instantiated directly.
> ... Surely a class should be responsible for storing and finding > method, but some other thing, call it a Dispatcher object
or the metaclass object?
> Of course, if you have OO languages that have weird dispatch rules, you > might need to have multiple dispatchers hanging around but (I'd argue) > you're still better attaching them to classes using composition rather > than inheritance.
The C<isa> relationship inside PMCs is just a way to define that missing vtable functions are filled in with the parent's function. You an think of that the PMCs (ParrotObject, ParrotClass, delegate, and possibly mmd_default) together build the metaclass object (or dispatcher in your terms). The ParrotObject PMC just holds the necessary information to instantiate the HLL object.
Dan Sugalski wrote: > So, if I understand this right (and I may well not), when you > instantiate a metaclass you get a class, and when you instantiate a > class you get an object, and since anything you instantiate is an object > anyway that means classes are objects. I'm not entirely sure if > metaclasses are objects, but it seems to lack a certain amount of > egregious symmetry if it doesn't.
Meta classes break the "every class has a super class"-chain by inheriting from themselves. A natural meta class is the word noun. It describes a class of words and it is an instance of that class.
Malte Ubl wrote: > Dan Sugalski wrote: > > So, if I understand this right (and I may well not), when you > > instantiate a metaclass you get a class, and when you instantiate a > > class you get an object, and since anything you instantiate is an object > > anyway that means classes are objects. I'm not entirely sure if > > metaclasses are objects, but it seems to lack a certain amount of > > egregious symmetry if it doesn't. > Meta classes break the "every class has a super class"-chain by > inheriting from themselves. A natural meta class is the word noun. It > describes a class of words and it is an instance of that class.
In many languages like Squeak, this is not quite true. Classes are instances of Metaclass, and Metaclass is an instance of the Metaclass class, so the circular reference occurs in the instantiation hierarchy and not the inheritance hierarchy. So for some instance abc of the ABC class which inherits from Object, we have
(instance hierarchy) abc -> ABC -> ABC class -> Metaclass -> Metaclass class -> Metaclass
(inheritance hierarchy (simplified from Squeak)) ABC -> Object Metaclass -> Behavior -> Object Metaclass class -> Behavior -> Object
In languages such as Squeak, Smalltalk and Actor, "every" class does not inherit from themselves. There are usually only two "special" classes, like Metaclass and Behavior. Metaclass instantiates the class of classes and in turn is an instance of one of its objects, the Metaclass class.
Behavior is the superclass of all classes which in turn has Object as its Superclass. In this way, all objects are instances of classes which have Object as their superclass.
Metaclasses are objects (in languages like Squeak, Smalltalk and Actor).
Mark Solinski
"The fox knows many tricks. The hedgehog knows one. One good one." -- Archilochus
On Sun, Mar 14, 2004 at 02:32:44PM +0100, Leopold Toetsch wrote:
: Why? A ParrotClass is responsible for the method dispatch. The ParrotObject : inherits that behavior.
In Perl 6 terms we'd prefer to say that ParrotClass "does" the Dispatch role, and so does ParrotObject, but to call it inheritance is misleading. If you want to implement roles internally as a mild form of inheritance, that's okay, but it *will* confuse people, and will have to be explained repeatedly.
Basically, Perl 6 allows dispatch on anything that supports the Dispatch role, whether that happens to be called a class or an object or both. The funny thing about ordinary classes is that they dispatch ordinary method calls to themselves rather than to their metaclass. It takes special syntax to "go meta". (That might or might not map well onto Ruby's Foo vs Foo' distinction.)
>On Sun, Mar 14, 2004 at 02:32:44PM +0100, Leopold Toetsch wrote: >: Why? A ParrotClass is responsible for the method dispatch. The ParrotObject >: inherits that behavior.
>In Perl 6 terms we'd prefer to say that ParrotClass "does" the >Dispatch role, and so does ParrotObject, but to call it inheritance >is misleading. If you want to implement roles internally as a mild >form of inheritance, that's okay, but it *will* confuse people, and >will have to be explained repeatedly.
Roles are going to get implemented as inheritance--so far I've seen no technical reason not to, and quite a number of reasons to do so. People can cope, if they're looking this deeply. -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
On Tue, 2004-03-16 at 06:42, Dan Sugalski wrote: > Roles are going to get implemented as inheritance--so far I've seen > no technical reason not to, and quite a number of reasons to do so. > People can cope, if they're looking this deeply.
Out of curiosity, what are those reasons?
I'm not sure how you avoid the case where you accidentally inherit behavior from a parent role where you shouldn't, unless you set one bit that says "inherits" and another bit that says "but not really".
>> Roles are going to get implemented as inheritance--so far I've seen >> no technical reason not to, and quite a number of reasons to do so. >> People can cope, if they're looking this deeply.
>Out of curiosity, what are those reasons?
>I'm not sure how you avoid the case where you accidentally inherit >behavior from a parent role where you shouldn't, unless you set one bit >that says "inherits" and another bit that says "but not really".
Unless I missed something, child classes inherit parent class roles, so if I have Foo with a role of X, and Bar inherits from Foo, Bar does the X role. Looks like inheritance to me... -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
>> Unless I missed something, child classes inherit parent class roles, >> so if I have Foo with a role of X, and Bar inherits from Foo, Bar >> does the X role. Looks like inheritance to me...
>That's normal inheritance and that's fine.
>Consider instead a class that does a role but does not inherit anything.
I am.
You're mixing things up some here. Larry's way of presenting roles at the language level likely is contributing.
A class >does< X if X is on the does list of the class or any of the parents of the class. This class then does the role X.
A class >isa< X if X in on the isa list of the class or any of the parents of the class.
Larry's role stuff muddles that up. You end up with a Role X which is the class X with X on the does list. Therefore X isa X *and* does X. If you inherit from X your class does X as well.
Alternately, your class can just declare that it does X and thus *only* put X on its does list. Then that class or any child class is noted as doing X, without actually being an X.
That's how things are going to work. It's possible (even likely) that we don't have the does searching right, but if not we just need to fix that. -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
On Tue, 2004-03-16 at 10:25, Dan Sugalski wrote: > Unless I missed something, child classes inherit parent class roles, > so if I have Foo with a role of X, and Bar inherits from Foo, Bar > does the X role. Looks like inheritance to me...
That's normal inheritance and that's fine.
Consider instead a class that does a role but does not inherit anything.
Roles should not affect method dispatch. Inheritance should.
Compilation -----------
- Roles - apply methods from a role - mark that the applying class does the role
- Inheritance - mark that the inheriting class inherits
Method dispatch ---------------
- Roles - do not look at any applied roles for methods not present in the invocant's class
- Inheritance - look in the parent classes for methods not present in the invocant's class
Type checking -------------
- Roles - look at the applied roles for a match
- Inheritance - look at the parent classes for a match
That all leads me to believe there are two separate questions:
1) What is the type of this object (what kind of behavior can I expect from it)?
2) Where does it get its methods?
I think mixing those two in one mechanism would be a mistake.
On Tue, 2004-03-16 at 11:46, Dan Sugalski wrote: > A class >does< X if X is on the does list of the class or any of the > parents of the class. This class then does the role X. > A class >isa< X if X in on the isa list of the class or any of the > parents of the class. > Alternately, your class can just declare that it does X and thus > *only* put X on its does list. Then that class or any child class is > noted as doing X, without actually being an X.
As long as there are separate does and isa lists, it can work right.
Just to be sure, if Class Y inherits from Class X, is X on Y's does list?
>> A class >does< X if X is on the does list of the class or any of the >> parents of the class. This class then does the role X.
>> A class >isa< X if X in on the isa list of the class or any of the >> parents of the class.
>> Alternately, your class can just declare that it does X and thus >> *only* put X on its does list. Then that class or any child class is >> noted as doing X, without actually being an X.
>As long as there are separate does and isa lists, it can work right.
>Just to be sure, if Class Y inherits from Class X, is X on Y's does >list?
If class X does X, then yes. (I *really* should've picked better names) Classes don't by default do themselves, so if you had a top-level class Foo that didn't mark itself as doing anything, an object of class Foo would return true for an isa('Foo') check but false for a does('Foo') check.
Classes and roles don't automatically share the same namespace. -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
On Tue, Mar 16, 2004 at 02:57:07PM -0500, Dan Sugalski wrote:
: Classes and roles don't automatically share the same namespace.
I think they do. I want to be able to tell the moment I compile it whether Foo is a class or a role or (a bareword that will not succeed in being either). Roles are just funny packages, just like classes, modules, and er, packages.
>On Tue, Mar 16, 2004 at 02:57:07PM -0500, Dan Sugalski wrote: >: Classes and roles don't automatically share the same namespace.
>I think they do. I want to be able to tell the moment I compile it >whether Foo is a class or a role or (a bareword that will not succeed >in being either). Roles are just funny packages, just like classes, >modules, and er, packages.
Only if you mix up the implementation and promise, which we're not doing at this level. -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk
> At 11:51 AM -0800 3/16/04, chromatic wrote: > >Just to be sure, if Class Y inherits from Class X, is X on Y's does > >list?
> If class X does X, then yes. (I *really* should've picked better > names) Classes don't by default do themselves, so if you had a > top-level class Foo that didn't mark itself as doing anything, an > object of class Foo would return true for an isa('Foo') check but > false for a does('Foo') check.
> Classes and roles don't automatically share the same namespace.
This means that the Perl 6 compiler will have to set does() on all classes or class-manipulations. As you suggest, we're not mixing up implementation (how does he do it?) with promise (he says he'll do it) -- signature type checking has to respect roles, not inheritance.
(Followups on why this must be -> p6l.)
If Parrot doesn't do this by default, that's fine in the sense that it'll at least make the mechanism available, but Perl 6 has to do it.
>> >Just to be sure, if Class Y inherits from Class X, is X on Y's does >> >list?
>> If class X does X, then yes. (I *really* should've picked better >> names) Classes don't by default do themselves, so if you had a >> top-level class Foo that didn't mark itself as doing anything, an >> object of class Foo would return true for an isa('Foo') check but >> false for a does('Foo') check.
>> Classes and roles don't automatically share the same namespace.
>This means that the Perl 6 compiler will have to set does() on all >classes or class-manipulations. As you suggest, we're not mixing up >implementation (how does he do it?) with promise (he says he'll do it) >-- signature type checking has to respect roles, not inheritance.
This is... odd. And it'll cause interesting problems with inter-language inheritance, since the perl 6 compiler will be the only thing doing this, and I think generally all the other languages (including perl 5) will be doing type checking based on the inheritance hierarchy rather than the interfaces, but...
>(Followups on why this must be -> p6l.)
Don't care, really. :) -- Dan
--------------------------------------"it's like this"------------------- Dan Sugalski even samurai d...@sidhe.org have teddy bears and even teddy bears get drunk