Re: overloaded operator calling conventions

2004-12-16 Thread Sam Ruby
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

2004-12-16 Thread Sam Ruby
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

2004-12-16 Thread Leopold Toetsch
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

2004-12-16 Thread Leopold Toetsch
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

2004-12-15 Thread Leopold Toetsch
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

2004-12-15 Thread Luke Palmer
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

2004-12-15 Thread Leopold Toetsch
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

2004-12-14 Thread Leopold Toetsch
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

2004-12-13 Thread Dan Sugalski
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

2004-12-11 Thread Sam Ruby
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

2004-12-10 Thread Leopold Toetsch
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

2004-12-09 Thread Leopold Toetsch
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