Re: overloaded operator calling conventions
Leopold Toetsch wrote: Sam Ruby wrote: A few observations, first from an Parrot Internal perspective... in general, the code for the opcodes tend to do things like the following: $1-vtable-get_string(interpreter, $1) Note that the object tends to be repeated as the first argument. It often the case that the first argument matches the object on which the dispatch is based, but it is not necessarily so. In particular, these two values will be different whenever the implementation of a method wants to invoke the equivalent of SUPER(args). Not quite. That'll be class_self.__super(args) so again the invocant is the first argument after interpreter. Believe it or not, I think we are agreeing. To invoke a method on an object using Parrot Calling Conventions, P2 needs to be the object used for dispatch purposes, and P5 needs to be the actual object. In many cases, they will be the same, but in some cases they will differ. This isn't obvious from PDD03, but a simple clarification would take care of that. - Sam Ruby
Re: overloaded operator calling conventions
Leopold Toetsch wrote: Luke Palmer [EMAIL PROTECTED] wrote: Leopold Toetsch writes: Why do we have the special notion of current_object in the first place? Why not just pass all in as P5, P6, ...? I agree that this is the way to go. Especially if we have some marker somewhere that tells us that we were called as a method. Does the Perl6 compiler know function signatures at compile time? S06 states: Passing two many or too few invocants is a fatal error. For a runtime check we'd need additionally the MMD object count. A few observations, first from an Parrot Internal perspective... in general, the code for the opcodes tend to do things like the following: $1-vtable-get_string(interpreter, $1) Note that the object tends to be repeated as the first argument. It often the case that the first argument matches the object on which the dispatch is based, but it is not necessarily so. In particular, these two values will be different whenever the implementation of a method wants to invoke the equivalent of SUPER(args). As to compile time/runtime checks Python will need complete function signature (not just the number, but the actual names of each of the paramters) at runtime. At the moment, I'm storing this as a property. - Sam Ruby
Re: overloaded operator calling conventions
Sam Ruby wrote: A few observations, first from an Parrot Internal perspective... in general, the code for the opcodes tend to do things like the following: $1-vtable-get_string(interpreter, $1) Note that the object tends to be repeated as the first argument. It often the case that the first argument matches the object on which the dispatch is based, but it is not necessarily so. In particular, these two values will be different whenever the implementation of a method wants to invoke the equivalent of SUPER(args). Not quite. That'll be class_self.__super(args) so again the invocant is the first argument after interpreter. leo
Re: overloaded operator calling conventions
Sam Ruby wrote: Leopold Toetsch wrote: class_self.__super(args) so again the invocant is the first argument after interpreter. Believe it or not, I think we are agreeing. *g* To invoke a method on an object using Parrot Calling Conventions, P2 needs to be the object used for dispatch purposes, and P5 needs to be the actual object. In many cases, they will be the same, but in some cases they will differ. Yep. Another good reason to pass the object as P5 (too). This isn't obvious from PDD03, but a simple clarification would take care of that. I'm still waiting for Dan's decision. - Sam Ruby leo
Re: overloaded operator calling conventions
Dan Sugalski [EMAIL PROTECTED] wrote: The note here is that Parrot's MMD function signature for binary ops doesn't match what Python needs. Parrot is: void binary_mmd_op(pmc left, pmc right, pmc dest) where Python is: pmc dest = left.add(pmc right) Perl6 allows (according to S06) for calling a multi method: mulit sub f($a, $b: $c) { ... } function call syntax: f($x, $y, $z); as well as method call syntax on the first invocant: $x.f($y, $z); Perl5 as well as Python have the invocant as the first function argument. We could now of course translate at runtime between these call schemes (prepend P2 and shift all up or move P5 into current_object and shift arguments down). But this takes some time. Why do we have the special notion of current_object in the first place? Why not just pass all in as P5, P6, ...? leo
Re: overloaded operator calling conventions
Leopold Toetsch writes: Why do we have the special notion of current_object in the first place? Why not just pass all in as P5, P6, ...? I agree that this is the way to go. Especially if we have some marker somewhere that tells us that we were called as a method. Luke
Re: overloaded operator calling conventions
Luke Palmer [EMAIL PROTECTED] wrote: Leopold Toetsch writes: Why do we have the special notion of current_object in the first place? Why not just pass all in as P5, P6, ...? I agree that this is the way to go. Especially if we have some marker somewhere that tells us that we were called as a method. Does the Perl6 compiler know function signatures at compile time? S06 states: Passing two many or too few invocants is a fatal error. For a runtime check we'd need additionally the MMD object count. Luke leo
Re: overloaded operator calling conventions
Dan Sugalski [EMAIL PROTECTED] wrote: At 7:45 AM +0100 12/11/04, Leopold Toetsch wrote: Thinking more about that it seems that we don't have much chance to keep the current scheme that the destination is passed in. I fully expected this to be an issue. Perl 5 and perl 6 are going to have different conventions, (and I think there may well be at least two separate ones for perl 6, but I may be misrememebering) Ruby doesn't match, and neither do any of the other languages. [ ... ] Nothing much for it -- no matter what we choose it's going to be wrong for someone, so the sensible thing to do is choose the scheme that works best for the underlying model (which we have) and leave it to compilers and class libraries to translate to their own preferred form. I think we're likely to find that the scheme we have catches on reasonably well once we've hit release and start seeing widespread use. Shouldn't the underlying model be at least near the expectations of HLLs we want to support? It doesn't buy us anything, if we force all languages to create wrappers. And works best for the underlying model just means the code exists. Finally the current implementation can't handle singletons (like PyNone) as a return result from such opcodes. leo
Re: overloaded operator calling conventions
At 7:45 AM +0100 12/11/04, Leopold Toetsch wrote: Thinking more about that it seems that we don't have much chance to keep the current scheme that the destination is passed in. (This is probably out of order -- I've a lot of mail I'm backed up on unfortunately, but since it was CC'd directly to me I'll take it) The note here is that Parrot's MMD function signature for binary ops doesn't match what Python needs. Parrot is: void binary_mmd_op(pmc left, pmc right, pmc dest) where Python is: pmc dest = left.add(pmc right) And, as you can see, the difference is more than just python creating a destination where we require one to be passed in -- it's a method call as well. I fully expected this to be an issue. Perl 5 and perl 6 are going to have different conventions, (and I think there may well be at least two separate ones for perl 6, but I may be misrememebering) Ruby doesn't match, and neither do any of the other languages. I think there was some discussion back when this was first batted around, but there might not have been. The short answer here is to cope: that is, when installing an MMD function, including one of the default MMD functions for a class, a language needs to generate the appropriate wrapper function if necessary to translate between what parrot provides and what the language itself wants. Nothing much for it -- no matter what we choose it's going to be wrong for someone, so the sensible thing to do is choose the scheme that works best for the underlying model (which we have) and leave it to compilers and class libraries to translate to their own preferred form. I think we're likely to find that the scheme we have catches on reasonably well once we've hit release and start seeing widespread use. -- Dan --it's like this--- Dan Sugalski even samurai [EMAIL PROTECTED] have teddy bears and even teddy bears get drunk
Re: overloaded operator calling conventions
Leopold Toetsch wrote: Comments? leo As enjoyable as this discussion has been, I'd like to ask that it be put on hold for a few days. I've nearly got all the previously defined languages/python/t/basic tests running, and once they are running, I'd like to do a bit of refactoring and documentation of a complete proposal on how this can work. - Sam Ruby P.S. Peeking at the current implementation may not be wise, as a number of shortcuts were taken (example: int objects behave as their own class, etc), which confuses the picture.
Re: overloaded operator calling conventions
Leopold Toetsch [EMAIL PROTECTED] wrote: [ fullquote ] A recent discussion with Sam has shown that the current calling conventions for overloaded operators don't match Python semantics (nor Perl6 when I interpret S06 and S13 correctly). The difference is that Parrot is passing in the destination argument while these languages are returning the operator result. E.g. def myadd(self, r): return self - r class I(int): __add__ = myadd i = I(44) print i, i + 2 Parrot is expecting and calling a subroutine like: .sub __add .param left .param right .param dest ... .end and it's run in void context. This calling convention matches the add opcode, where the destination argument has to exist. But it's not suited for HLLs as the HLL compiler can't track the subroutine usage back to overloading and adjust the emitted code. A short-term solution would be to evaluate the return result (if the function returns something) and assign that value to the dest argument. Parrot would call .sub __add .param left .param right .param dest # HLL can use it or create a temp ... .return(temp) # or .return (dest) if HLL knows about dest .end while this is still a bit sub-optimal because of the additional temp, the HLL might create, it would at least match the semantics. A more radical change would be to just adapt these opcodes to create an appropriate PMC with the result. Thinking more about that it seems that we don't have much chance to keep the current scheme that the destination is passed in. a Python snippet: def myadd(self, r): return I(self.v - r) class I(object): __add__ = myadd def __init__(self, v): self.v = v def __repr__(self): return I(%d) % self.v i = I(44) j = i + 2 k = j + 2 print i, j, k The add opcode is overloaded and returns a new object of class I. The current scheme is: $P0 = new Undef $P0 = add i, 2 *If* the HLL compiler can somehow make a connection between the myadd function as an overloaded add opcode, it could try to morph the passed in destination argument. The whole morph code is bulky and slow, changing one PMC into an arbitrary different one needs deallocating the old one (what happens with finalizers?) and constructing a new one in place. But I doubt that a compiler can track such a usage (myadd could be in a different file and imported). That means we are pre-constructing a LHS PMC for nothing with all implications on GC and performance. Second: looking at pmc.c:pmc_reuse() the code isn't able to and can't never return a singleton as the result of an operation, because that would mean to change the address of the argument, where only it's pointer is passed. Comments? leo leo
overloaded operator calling conventions
A recent discussion with Sam has shown that the current calling conventions for overloaded operators don't match Python semantics (nor Perl6 when I interpret S06 and S13 correctly). The difference is that Parrot is passing in the destination argument while these languages are returning the operator result. E.g. def myadd(self, r): return self - r class I(int): __add__ = myadd i = I(44) print i, i + 2 Parrot is expecting and calling a subroutine like: .sub __add .param left .param right .param dest ... .end and it's run in void context. This calling convention matches the add opcode, where the destination argument has to exist. But it's not suited for HLLs as the HLL compiler can't track the subroutine usage back to overloading and adjust the emitted code. A short-term solution would be to evaluate the return result (if the function returns something) and assign that value to the dest argument. Parrot would call .sub __add .param left .param right .param dest # HLL can use it or create a temp ... .return(temp) # or .return (dest) if HLL knows about dest .end while this is still a bit sub-optimal because of the additional temp, the HLL might create, it would at least match the semantics. A more radical change would be to just adapt these opcodes to create an appropriate PMC with the result. Comments? leo