Programming, Software and Code

Calling Conventions Work



Yesterdays ParrotSketch meeting was quite interesting. We got through the reports quickly, and I asked a bunch of questions that got quick answers, and then we moved on to a discussion about Calling Conventions. My planning for Context PMCs is just a small part in an ongoing stream of refactors, changes, and improvements to this critical subsystem.

Allison, as I've mentioned before is getting this party started with some serious refactors to the way function calls happen. All calling paths are being migrated to use CallSignature PMCs, which contain the call signature, the list of passed arguments, the list of returns, and assorted other information. Patrick and Jonathan were also asking for some features that have been on the TODO list for a while: named-only parameters, named-if-possible parameters, and an ability to override the invoke VTABLE from PIR without TEH FAILZ.

The current semantics of named parameters is to fill in the list of parameters with the given positionals first, and then fill in any extra named parameters that have been provided. However, this system doesn't support a case where a parameter should not be filled by a positional parameter in any circumstances; that is if the parameter is "named only". It also doesn't count the case where we want a parameter to attempt to be be filled by a named if available, and a positional otherwise (a reverse of the current semantics). Apparently this issue was discussed at length on the mailing list, and I need to review all that. I think, based on what we talked about yesterday, that the parameter declaration flags are going to look like this:
Another issue that was brought up, which is near and dear to my heart, is improving PIR overrides of the "invoke" VTABLE. The things currently work, the invoke VTABLE does not have access to the self keyword. Without being able to easily get a hold of the PMC reference that was invoked, it is useless and nonsensical to try and override invoke from PIR.

The reasons why we have such difficulty with this particular issue because of two reasons: The way that we pass arguments around in PIR, and the semantic differences between different notions of the word "self". Consider two cases:

$P0()

$P0.'foo'()

In the first case, the $P0 object is being invoked, which will call the invoke VTABLE on it. In the second case, a method on $P0 is invoked instead. These are obviously two different cases, so it doesnt make sense that within the subroutine body we refer to this quantity by the same name both times. In the first example, $P0 is the subroutine being invoked, and in the second it is the invocant on which a subroutine (method) is invoked. If we invoke a method directly as a subroutine without an invocant, do we expect that self will refer to anything at all, much less the Sub object itself? And when we do invoke the method on the invocant, we obviously expect that self refers to the invocant. Seeing the difference here, we have to assume that Sub should not change it's behavior based on the way it is called, or should even be aware of the way it's invoked. That's bad for encapsulation.

The solution here is to provide two separate access points for the two separate concepts: a self keyword which represents the invocant, and something else (like, say, this_sub) that will refer to the currently executing Sub PMC. Normal methods will use the former without breaking anything. The invoke VTABLE can use the later to suddenly become useful.

There are two methods on the horizon to make this possible: Allisons work where we will have a CallSignature PMC made available from which the current invocant and the current Sub PMCs can be extracted, and my work where we will have access to a Context PMC which contains these same pieces of information. In the long term future, as I mentioned in my previous post, these two PMC types are likely to be merged together.

Allison says her stuff is scheduled to land by the 1.4 release. I'll definitely be doing my Context work shortly after that (I don't want to be developing two separate in-depth branches on the same subsystem simultaneously, to prevent TEH FAILZ). So things will get moving soon on all this work, and there will be much rejoicing.

Comments

I kind of like the way that Python handles methods. They are like plain functions, but accept an explicit 'self' argument as the first one. I don't know if something like this would make sense for parrot.

class A:
def set_n (self, n):
self.n = n

a = A() # creates new instance

a.set_n(1) # a.n = 1
A.set_n(a, 2) # a.n = 2

s = A.set_n
s(a, 3) # a.n = 3

t = a.set_n
t(4) # a.n = 4

Yes, that's also similar to the Perl5 way where the "self" is the first argument, although certain syntactic elements obscure that fact:

$a->foo(1, 2, 3) # Same as foo($a, 1, 2, 3)

sub foo {
my ($self, $a, $b, $c) = @_;
}

That much at least accounts for passing the invocant in the case of a method call, but doesn't cover the case of overriding the invoke call for a sub-like object. Think about an indirect method call (I'm using Perl syntax to make it clear which things here are objects):

$a->$b()

Where $a is the invocant object and $b is the subroutine object. In this case, which of these two values should be called "self" in the subroutine at runtime?



This entry was originally posted on Blogger and was automatically converted. There may be some broken links and other errors due to the conversion. Please let me know about any serious problems.