Re: Python PMC's
Leopold Toetsch wrote: > > Above Parrot interface function tries to locate Sub objects in the > namespace of the invocant's class and in the MRO of it. When you go back > to the PIR translation of your code there is e.g. > > class Foo: > def g(self,y): > > I have translated this to: > > .namespace ["Foo"] > .sub g > .param pmc self > > Therefore all the static / default / common "methods" inside Python code > would be callable with the plain Parrot method call syntax. > > The classname contributes the namespace. The sub declaration creates an > entry in that namespace, which is retrievable as: > > func = findglobal "Foo", "g" Honestly, I don't believe that that is workable for Python. Modules are global in Python. Classes are lexically scoped. All subs emitted by Pirate are @anon. Modules are namespaces and can contain classes, functions, and variables. The way to retrieve a method from a Python class defined in module __main__ would be: $P1 = findglobal "__main__", "Foo" getattribute $P2, $P1, 'g' - Sam Ruby
Re: [pirate] Re: Python PMC's
On Wed, 24 Aug 2005, Sam Ruby wrote: [huge cut] > Below is from the sample that Leo provided. > > > # print foo.f(2) > > > > # emulate python find_name, which checks attributes too > > push_eh m_nf > > $P0 = foo."f"(2) > > clear_eh > > goto m_f > > m_nf: > > # getattribute would also check if __get__ is there > > $P1 = getattribute foo, "f" > > $P0 = foo.$P1(2) > > m_f: > > print_item $P0 > > print_newline > > Note that the code above would need to be emitted for *every* method > call, as there is no way of knowing at compile time what property you > will get and how it is defined. Hey Sam, I agree with what you're saying in this thread. This is slightly off topic, but I wanted to point something out. In general, python has so many places where things have to be dynamic that you really can't know this kind of thing at compile time, especially if you allow for eval/exec, or if you allow the code to be used as a module. However if you treat the code as a closed system, *and* you have access to python at compile time, then we can optimize away a lot of these questions. For example, in your original code: def f(x,y): return y class Foo: f = f def g(self,y): return y foo = Foo() g=foo.g print f(0,1) print foo.f(2) print g(3) Once you know how python works, it's *obvious* that this prints 1,2,3. I see no reason why the compiler couldn't figure this out up front just by walking the tree. In fact, a good optimizing compiler would see the "return y" lines and just get rid of those methods completely. I'd like to allow for the ability to do certain optimizations like this up front, sacrificing flexibility for speed. I know there are many programs that won't allow for this, but for the ones that do, I'd like to be able to do a sort of static compile like this. In other words, sometimes a python-like language is a desirable thing. (But of course this should all be optional so that we can also be 100% python compatible) Sincerely, Michal J Wallace Sabren Enterprises, Inc. - contact: [EMAIL PROTECTED] hosting: http://www.cornerhost.com/ my site: http://www.withoutane.com/ -
Re: Python PMC's
On Aug 24, 2005, at 23:34, Sam Ruby wrote: Leopold Toetsch wrote: Note that you would then be caching the results of a curried function call. This result depends not only on the method string, but also on the particular object upon which it was invoked. No the "inner" Parrot_find_method_with_cache just caches the method for a specific class (obeying C3 method resolution order as in Python). There is no curried function at that stage. Where does Parrot_find_method_with_cache get this data? Remember, in Python, there are no methods, there are only properties. Above Parrot interface function tries to locate Sub objects in the namespace of the invocant's class and in the MRO of it. When you go back to the PIR translation of your code there is e.g. class Foo: def g(self,y): I have translated this to: .namespace ["Foo"] .sub g .param pmc self Therefore all the static / default / common "methods" inside Python code would be callable with the plain Parrot method call syntax. The classname contributes the namespace. The sub declaration creates an entry in that namespace, which is retrievable as: func = findglobal "Foo", "g" And as the namespace is exactly the classname this is exactly what find_method for a "Foo" object does, when trying to locate a method named "g" (I'm omitting here further lookups according to MRO due to other parents). The actual namespace that the classes find_method looks into can further be finetuned with the vtable function VTABLE_namespace_name(interp, class). Other methods, like the one defined by "f = f", would of course need a fallback to attribute lookup (please can we keep the term attribute, which is also used in CPython) - and not property). Therefore the 'find_method' inside Py code should also consult the attributes of the object (and yes properties for per object overrides). Well, this is at least my understanding how it could work. Of course, there is a find_method VTABLE entry, and the implementation of this function calls __get__ which performs the curry function, per the Python specifications. Yes. But currying isn't needed for a plain method call, when the __get__ isn't user defined. Why first curry the function in the first place, create a new curried sub PMC, then during invocation shift arguments, and eventually run it. This isn't the common case. Even if CPython does it like this now (and Python folks are discussing an optimized opcode on pthon-dev), it's not better or more correct - just the result is important. And that works as is now for Parrot method calls. Does Parrot_find_method_with_cache cache the results of the previous call to find_method? It does one dynamic lookup and caches per class/method the result. The cache is invalidated by a store_global (which could influence the dynamic search result). - Sam Ruby leo
Re: Python PMC's
Leopold Toetsch wrote: > >> Note that you would then be caching the results of a curried function >> call. This result depends not only on the method string, but also on >> the particular object upon which it was invoked. > > No the "inner" Parrot_find_method_with_cache just caches the method for > a specific class (obeying C3 method resolution order as in Python). > There is no curried function at that stage. Where does Parrot_find_method_with_cache get this data? Remember, in Python, there are no methods, there are only properties. Of course, there is a find_method VTABLE entry, and the implementation of this function calls __get__ which performs the curry function, per the Python specifications. Does Parrot_find_method_with_cache cache the results of the previous call to find_method? - Sam Ruby
Re: Python PMC's
On Aug 24, 2005, at 19:45, Sam Ruby wrote: Leopold Toetsch wrote: Sam Ruby wrote: The return value is a callable sub. More precisely: a curried function call. This is an important distinction; to see why, see below. A callable sub may be of course a curried one - yes. The interesting optimizations are: - cache the find_method in the global method cache. This happens already, if the method string is constant. Note that you would then be caching the results of a curried function call. This result depends not only on the method string, but also on the particular object upon which it was invoked. No the "inner" Parrot_find_method_with_cache just caches the method for a specific class (obeying C3 method resolution order as in Python). There is no curried function at that stage. - use a PIC [1] (which is tied to the opcode) and cache the final resut of find_method (or any similar lookup) Again, the results will depends on the object. Yes. The nice thing with a PIC is that it is per bytecode location. You have a different PIC and a different cache for every call site. The prologue of PIC opcode is basically: if cache.version == interpreter.version: (cache.function)(args) else: # do dynamic lookup # update cache then repeat The 'version' compare depends on the cached thingy, and is more explicit in individual implementations. But the principle remains always the same: you create a unique id that depends on the variables of the lookup and remember it. Before invoking the cached result you compare actual with cached ids. If there is a cache miss, there are 2 or 3 more cache slots to consult before doing just the dynamic original scheme again (and maybe rewrite the opcode again to just the dynamic one in case of too many cache misses). src/pic.c and ops/pic.ops have an implementation for sub Px, Py - just for fun and profit to show the performance in the MOPS benchmark ;-) The important part in ops/pic.ops is: lr_types = (left->vtable->base_type << 16) | right->vtable->base_type; if (lru->lr_type == lr_types) { runit_v_pp: ((mmd_f_v_pp)lru->f.real_function)(interpreter, left, right); (the lru is part of the cache structure, above code will be only run, when both types are <= 0x) MMD depends on the two involved types. This is compared before calling the cached function directly. As the cache is per bytecode location, there is a fair chance (>95 %) that the involved types for this very code location are matching. The same is true for plain method calls. The callee depends on the invocant and the method name, which is usually a constant. Therefore you can compare the cached version with the actual invocant type and normally, with a match, just run the cached function immediately. Currying is only important for placing the 'self' into the arguments - the actual lookup was already done earlier and doesn't influence the called function. Actually a curried subroutine isa 'Sub' object already and invoked directly withouth any further method lookup. There is no caching involved in the call of a curried sub. - run the invoked Sub inside the same run loop - specifically not within Parrot_run_meth_fromc_args I don't understand this. (Note: it may not be important to this discussion that I do understand this - all that is important to me is that it works, and somehow I doubt that Parrot_run_meth_fromc_args cares whether a given function is curried or not). There are two points to be considered: - currying: the effect is that some call arguments (in this special case the object) are already fixed. The argument passing code has therefore the duty to insert these known (and remembered) arguments into the params for the callee. For the BoundMethod this is of course, shift all arguments up by one, and make the object 'self' the first param of he sub. - the second point is only related to call speed aka optimization. It's just faster to run a PIR sub in the same run loop, then to create a new run loop. The above works because PyInt is a constant. It probably can be extended to handle things that seem unlikely to change very rapidly. Yes. That's the 'trick' behind PIC. It works best the more constant the items are. But as said above, method names and invocants usually don't vary *per byecode location*. Literature states ~95 % of method calls are monomorphic (one type of invocant), and 99,5 % are cached within 4 cache slots. Look at some typical code a.'foo'(x) ... b.'bar'(y) Both method calls have a distinct cache. The method names are constant. Therefore the callee depends only on the invocant. The types of 'a' or 'b' are typically the same (except maybe inside compilers AST visit methods or some such). The same schme applies to plain method or attribute lookups. But the combination of decisions on how to handle the passing of the "self" parameter to a method, keeping find_
Re: Python PMC's
Leopold Toetsch wrote: > Sam Ruby wrote: > >> Leopold Toetsch wrote: > >>> A stripped down PIR-only, pythonless translation is below. > >> (example: classes aren't global in Python), > > Yes, of course. The stripped down means essential the absence of any > lexical handlings. But as you say, this doesn't matter for these sub and > method calls. Agreed. >> Note that Python provides __get__ methods for you on every function and >> method, i.e., it is *not* optional, getattribute will always succeed. > > That's fine. But if it's not overriden by user code, you exactly know > what it is doing. Therefore you can just emulate it, I think. I'm not as sure as you appear to be, but we can worry about this later. >>> # print foo.f(2) >>> >>># emulate python find_name, which checks attributes too >>>push_eh m_nf >>>$P0 = foo."f"(2) >>>clear_eh >>>goto m_f >>> m_nf: >>># getattribute would also check if __get__ is there >>>$P1 = getattribute foo, "f" >>>$P0 = foo.$P1(2) >>> m_f: > >> Note that the code above would need to be emitted for *every* method >> call, > > Above snippet should not be emitted for a method call, it just emulates > it in PIR. It should demonstrate how Python's find_method() could be > implemented: With that clarification, I agree in principle. > - try to find a method, else > - check attributes > - call __get__, if it is user provided > > (or whatever order CPython actually uses). What's in dynclass/pyclass.pmc matches Python's semantics closer. > The return value is a callable sub. More precisely: a curried function call. This is an important distinction; to see why, see below. >> Ideally, callmethodcc would result in one vtable call to enable >> interesting optimizations for those that care to provide it. The >> default implementation for this vtable entry could be a call to >> find_method and invoke. > > The interesting optimizations are: > - cache the find_method in the global method cache. > This happens already, if the method string is constant. Note that you would then be caching the results of a curried function call. This result depends not only on the method string, but also on the particular object upon which it was invoked. > - use a PIC [1] (which is tied to the opcode) and cache > the final resut of find_method (or any similar lookup) Again, the results will depends on the object. > - run the invoked Sub inside the same run loop - specifically > not within Parrot_run_meth_fromc_args I don't understand this. (Note: it may not be important to this discussion that I do understand this - all that is important to me is that it works, and somehow I doubt that Parrot_run_meth_fromc_args cares whether a given function is curried or not). > [1] polymorphic inline cache > > The important thing is that the method lookup and the subroutine > invocation happens from inside the runloop. Run cores that allow > rewriting of opcodes (prederefed and JIT) can insert faster equivalent > opcodes in these cases. > > Have a look at src/pic.c (which doesn't implement a lot - it's more a > proof of concept now). Anyway here is an example that is implemented: > > new P0, "PyInt"# new_p_sc > > When this opcodes gets execute the first time, the type number of the > class is looked up and then the opcode is replaced with the faster variant: > > new P0, 89 # new_p_ic - arbitrary number for PyInt > > The same scheme can be applied for all these opcodes that consist of any > kind of a lookup (MMD, methods, attributes, ...) and some further > action. The lookup is done once at runtime (and again only after cache > invalidation). The above works because PyInt is a constant. It probably can be extended to handle things that seem unlikely to change very rapidly. But the combination of decisions on how to handle the passing of the "self" parameter to a method, keeping find_method and invoke separated at the VTABLE level, and the semantics of Python make the notion of caching the results of find_method problematic. >> - Sam Ruby > > leo - Sam Ruby
Re: Python PMC's
Sam Ruby wrote: Leopold Toetsch wrote: A stripped down PIR-only, pythonless translation is below. (example: classes aren't global in Python), Yes, of course. The stripped down means essential the absence of any lexical handlings. But as you say, this doesn't matter for these sub and method calls. Note that Python provides __get__ methods for you on every function and method, i.e., it is *not* optional, getattribute will always succeed. That's fine. But if it's not overriden by user code, you exactly know what it is doing. Therefore you can just emulate it, I think. # print foo.f(2) # emulate python find_name, which checks attributes too push_eh m_nf $P0 = foo."f"(2) clear_eh goto m_f m_nf: # getattribute would also check if __get__ is there $P1 = getattribute foo, "f" $P0 = foo.$P1(2) m_f: Note that the code above would need to be emitted for *every* method call, Above snippet should not be emitted for a method call, it just emulates it in PIR. It should demonstrate how Python's find_method() could be implemented: - try to find a method, else - check attributes - call __get__, if it is user provided (or whatever order CPython actually uses). The return value is a callable sub. Ideally, callmethodcc would result in one vtable call to enable interesting optimizations for those that care to provide it. The default implementation for this vtable entry could be a call to find_method and invoke. The interesting optimizations are: - cache the find_method in the global method cache. This happens already, if the method string is constant. - use a PIC [1] (which is tied to the opcode) and cache the final resut of find_method (or any similar lookup) - run the invoked Sub inside the same run loop - specifically not within Parrot_run_meth_fromc_args [1] polymorphic inline cache The important thing is that the method lookup and the subroutine invocation happens from inside the runloop. Run cores that allow rewriting of opcodes (prederefed and JIT) can insert faster equivalent opcodes in these cases. Have a look at src/pic.c (which doesn't implement a lot - it's more a proof of concept now). Anyway here is an example that is implemented: new P0, "PyInt"# new_p_sc When this opcodes gets execute the first time, the type number of the class is looked up and then the opcode is replaced with the faster variant: new P0, 89 # new_p_ic - arbitrary number for PyInt The same scheme can be applied for all these opcodes that consist of any kind of a lookup (MMD, methods, attributes, ...) and some further action. The lookup is done once at runtime (and again only after cache invalidation). - Sam Ruby leo
Re: [pirate] Re: Python PMC's
I agree this following would be cool. However in the general case this type of code inference is HARD to do. I believe that the optimizations you are looking for would require a combination of type inference and graph reduction. PyPy may be the eventual answer. Don't get me wrong, I think it is great and the long term goal. As soon as we compile and run correctly, I'm all about optimizations. Good Insight Michal, Kevin, Michal Wallace wrote: > Hey Sam, >I agree with what you're saying in this >thread. This is slightly off topic, but >I wanted to point something out. > >In general, python has so many places >where things have to be dynamic that you >really can't know this kind of thing at >compile time, especially if you allow for >eval/exec, or if you allow the code to be >used as a module. > >However if you treat the code as a closed >system, *and* you have access to python at >compile time, then we can optimize away a >lot of these questions. > >For example, in your original code: > > def f(x,y): >return y > > class Foo: >f = f >def g(self,y): > return y > > foo = Foo() > > g=foo.g > > print f(0,1) > print foo.f(2) > print g(3) > > >Once you know how python works, it's *obvious* >that this prints 1,2,3. I see no reason why the >compiler couldn't figure this out up front just >by walking the tree. > >In fact, a good optimizing compiler would see the >"return y" lines and just get rid of those methods >completely. > >I'd like to allow for the ability to do certain >optimizations like this up front, sacrificing >flexibility for speed. I know there are many >programs that won't allow for this, but for the >ones that do, I'd like to be able to do a sort >of static compile like this. > >In other words, sometimes a python-like language >is a desirable thing. (But of course this should >all be optional so that we can also be 100% >python compatible) > >Sincerely, > >Michal J Wallace >Sabren Enterprises, Inc. >- >contact: [EMAIL PROTECTED] >hosting: http://www.cornerhost.com/ >my site: http://www.withoutane.com/ >- > >___ >pirate mailing list >[EMAIL PROTECTED] >http://cornerhost.com/mailman/listinfo/pirate >
Re: Python PMC's
Leopold Toetsch wrote: > Sam Ruby wrote: > >> Let me try again to move the discussion from subjective adjectives to >> objective code. Consider: > > [ example code ] > >> If you run this, you will get 1,2,3. >> >> When called as a function, f will return the value of the second >> parameter. When called as a method, the same code will need to return >> the value of the first parameter. > > The calls to f() work more or less w/o problems in branches/leo-ctx5. > >> I'm open to suggestions as to what PIR code should be emitted by Pirate >> to correspond to the above code. > > A stripped down PIR-only, pythonless translation is below. Thanks for doing this. Keeping the conversation anchored with code is very helpful. I have a number of unimportant to the topic at hand comments on that code (example: classes aren't global in Python), but for now, I'll focus on comments that are relevant to the topic at hand. >> The way this currently works with Pirate and the existing Py*.pmc >> closely follows the Python definition, which you can find here: >> >> http://users.rcn.com/python/download/Descriptor.htm#functions-and-methods > > Good explanation, thanks. > >> To illustrate how this works today, lets consider the call to foo.f(2) >> above. This involves both a find_method and an invoke. Lets consider >> each in turn: >> >> * pyclass.find_method first calls getprop on "f". >> * pyclass.find_method then calls "__get__" on the pyfunc returned, >>passing the object on which this method was called on. >> * pyfunc.__get__ creates a new PyBoundMeth object, saving both the >>original function and the object. >> * This PyBoundMeth object is then returned >> >> then: >> >> * pyboundmeth.invoke shifts all registers right, inserts the original >>object as the first parameter, and then calls invoke on the original >>function. >> >> Needless to say, this doesn't necessarily show off Parrot to its best >> advantage from a performance perspective. I tried a number of times to >> argue for a combined "find_and_invoke" VTABLE entry as much of the above >> can be optimized if you know that result of the find_method will only be >> used a single time for an invoke. If you like, you can scan the >> archives to see what reception this suggestion received. > > Well I think that the find_and_invoke is just the callmethodcc opcode as > used in my translation below. The call to __get__ and the BoundMethod > shouldn't be necessary, *if* their is no userdefined descriptor. That > is, when getattribute checks, if the attribute provides __get__ then > call it. Take a look at the definition of callmethodcc in ops/object.ops. It is a find_method followed by an invoke. It is the VTABLE operations that I would like to see combined. Note that Python provides __get__ methods for you on every function and method, i.e., it is *not* optional, getattribute will always succeed. Observe: [EMAIL PROTECTED]:~$ python Python 2.4.1 (#2, Mar 30 2005, 21:51:10) [GCC 3.3.5 (Debian 1:3.3.5-8ubuntu2)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> def f(): pass ... >>> f.__get__ >>> class c: ... def m(self): pass ... >>> c.m.__get__ >>> > The yet unsopported thing is > > g = foo.g > ... > g(3) > > which really needs a BoundSub object. But this is just a special case of > currying, or a special case of Perl6's .assuming(). Therefore I think > that Parrot should support this natively. I agree that BoundSub is a special case of currying, or what Perl6 calls assuming. I also agree that Parrot should support this natively. >> - Sam Ruby [snip] Below is from the sample that Leo provided. > # print foo.f(2) > > # emulate python find_name, which checks attributes too > push_eh m_nf > $P0 = foo."f"(2) > clear_eh > goto m_f > m_nf: > # getattribute would also check if __get__ is there > $P1 = getattribute foo, "f" > $P0 = foo.$P1(2) > m_f: > print_item $P0 > print_newline Note that the code above would need to be emitted for *every* method call, as there is no way of knowing at compile time what property you will get and how it is defined. In addition to increasing code size, it is error prone: for example, the above only appears to check for __get__ if the call fails, so the pie-thon b0 test will fail as hooking __get__ is integral to the way it provides a trace. Also, there are other reasons that exceptions may be thrown, and we are incurring the overhead of setting up exception blocks, etc again, this would apply to *every* method call. An alternate solution without these problems would be > # print foo.f(2) > $P0 = foo."f"(2) > print_item $P0 > print_newline Ideally, callmethodcc would result in one vtable call to enable interesting optimizations for those that care to provide it. The default implementation for this vtable entry could be a call to find_method and invoke. - Sam Ruby
Re: Python PMC's
Sam Ruby wrote: Let me try again to move the discussion from subjective adjectives to objective code. Consider: [ example code ] If you run this, you will get 1,2,3. When called as a function, f will return the value of the second parameter. When called as a method, the same code will need to return the value of the first parameter. The calls to f() work more or less w/o problems in branches/leo-ctx5. I'm open to suggestions as to what PIR code should be emitted by Pirate to correspond to the above code. A stripped down PIR-only, pythonless translation is below. The way this currently works with Pirate and the existing Py*.pmc closely follows the Python definition, which you can find here: http://users.rcn.com/python/download/Descriptor.htm#functions-and-methods Good explanation, thanks. To illustrate how this works today, lets consider the call to foo.f(2) above. This involves both a find_method and an invoke. Lets consider each in turn: * pyclass.find_method first calls getprop on "f". * pyclass.find_method then calls "__get__" on the pyfunc returned, passing the object on which this method was called on. * pyfunc.__get__ creates a new PyBoundMeth object, saving both the original function and the object. * This PyBoundMeth object is then returned then: * pyboundmeth.invoke shifts all registers right, inserts the original object as the first parameter, and then calls invoke on the original function. Needless to say, this doesn't necessarily show off Parrot to its best advantage from a performance perspective. I tried a number of times to argue for a combined "find_and_invoke" VTABLE entry as much of the above can be optimized if you know that result of the find_method will only be used a single time for an invoke. If you like, you can scan the archives to see what reception this suggestion received. Well I think that the find_and_invoke is just the callmethodcc opcode as used in my translation below. The call to __get__ and the BoundMethod shouldn't be necessary, *if* their is no userdefined descriptor. That is, when getattribute checks, if the attribute provides __get__ then call it. The yet unsopported thing is g = foo.g ... g(3) which really needs a BoundSub object. But this is just a special case of currying, or a special case of Perl6's .assuming(). Therefore I think that Parrot should support this natively. - Sam Ruby leo # # def f(x,y): #return y .sub f .param pmc x .param pmc y .return (y) .end # # class Foo: .sub create_Foo .local pmc self, Foo self = subclass "Py", "Foo" addattribute self, "f" addattribute self, "g" Foo = find_global "Foo", "Foo" store_global "Foo", Foo .end .namespace ["Foo"] .sub Foo .local pmc o o = new "Foo" .return (o) .end #f = f #def g(self,y): # return y .sub __init method .local pmc f, g f = find_name "f" setattribute self, "f", f g = find_name "g" setattribute self, "g", g .end .sub g .param pmc self .param pmc y .return (y) .end .namespace [""] .sub main @MAIN .local pmc foo, g init_python() create_Foo() # # foo = Foo() # foo = Foo() # g=foo.g # g = getattribute foo, "g" # TODO create bound Sub inside gettattribute like so ## $I0 = isa g, "Sub" ## unless $I0 goto no_sub ## $P0 = new .BoundSub, g ## assign $P0, foo ## g = $P0 ## no_sub: # print f(0,1) $P0 = f(0, 1) print_item $P0 print_newline # print foo.f(2) # emulate python find_name, which checks attributes too push_eh m_nf $P0 = foo."f"(2) clear_eh goto m_f m_nf: # getattribute would also check if __get__ is there $P1 = getattribute foo, "f" $P0 = foo.$P1(2) m_f: print_item $P0 print_newline # print g(3) #$P0 = g(3) #print_item $P0 #print_newline .end # .sub init_python .local pmc py, bs py = newclass "Py" .end
Re: Python PMC's
Chip Salzenberg wrote: > On Tue, Aug 23, 2005 at 07:15:41PM -0400, Sam Ruby wrote: > >>Leopold Toetsch wrote: >> >>>I've stated several times that calling conventions need changes to >>>properly support HLLs with minor success at these times. >> >>With the diversity of HLLs out there, I'm not certain that it is wise to >>declare one set of semantics "proper", thereby implicitly declaring all >>others "improper". > > Well, let's try "more coherent and less confusing", then, or maybe > just "better". HLLs don't have to expose the 'self' parameter if > they don't want to. Just extract it into a PMC register that's > never used. Let me try again to move the discussion from subjective adjectives to objective code. Consider: def f(x,y): return y class Foo: f = f def g(self,y): return y foo = Foo() g=foo.g print f(0,1) print foo.f(2) print g(3) If you run this, you will get 1,2,3. When called as a function, f will return the value of the second parameter. When called as a method, the same code will need to return the value of the first parameter. I'm open to suggestions as to what PIR code should be emitted by Pirate to correspond to the above code. The way this currently works with Pirate and the existing Py*.pmc closely follows the Python definition, which you can find here: http://users.rcn.com/python/download/Descriptor.htm#functions-and-methods To illustrate how this works today, lets consider the call to foo.f(2) above. This involves both a find_method and an invoke. Lets consider each in turn: * pyclass.find_method first calls getprop on "f". * pyclass.find_method then calls "__get__" on the pyfunc returned, passing the object on which this method was called on. * pyfunc.__get__ creates a new PyBoundMeth object, saving both the original function and the object. * This PyBoundMeth object is then returned then: * pyboundmeth.invoke shifts all registers right, inserts the original object as the first parameter, and then calls invoke on the original function. Needless to say, this doesn't necessarily show off Parrot to its best advantage from a performance perspective. I tried a number of times to argue for a combined "find_and_invoke" VTABLE entry as much of the above can be optimized if you know that result of the find_method will only be used a single time for an invoke. If you like, you can scan the archives to see what reception this suggestion received. Precisely emulating the behavior above is an important part of distinguishing a "Python-like" language from a true Python language, and is necessary to pass the b0 pie-thon test (grep for __get__ to see what I mean). As to whether this is "more coherent and less confusing", then, or maybe just "better"... well, lets just say that I'll leave the adjectives to others. - Sam Ruby
Re: Python PMC's
On Tue, Aug 23, 2005 at 07:15:41PM -0400, Sam Ruby wrote: > Leopold Toetsch wrote: > > I've stated several times that calling conventions need changes to > > properly support HLLs with minor success at these times. > > With the diversity of HLLs out there, I'm not certain that it is wise to > declare one set of semantics "proper", thereby implicitly declaring all > others "improper". Well, let's try "more coherent and less confusing", then, or maybe just "better". HLLs don't have to expose the 'self' parameter if they don't want to. Just extract it into a PMC register that's never used. -- Chip Salzenberg <[EMAIL PROTECTED]>
Re: Python PMC's
Leopold Toetsch wrote: > > On Aug 23, 2005, at 22:48, Sam Ruby wrote: > >>> From December 16, 2004: >> >> http://tinyurl.com/8smmq > > Sounds like a really ugly misunderstanding, the more that I've proposed > not to pass the object (P2 in old parlance) out of band. I've stated > several times that calling conventions need changes to properly support > HLLs with minor success at these times. With the diversity of HLLs out there, I'm not certain that it is wise to declare one set of semantics "proper", thereby implicitly declaring all others "improper". > What I've always said (and what is actually implemented now in the > branch) is that methods gets the object as its first argument. A typical > snippet in PIR code is now > > .sub __abs > .param pmc self > > or if denoted as method: > > .sub __abs method > # 'self' param automagically available > > The first argument of NCI or vtable methods is and was always SELF, > which usually is the invocant and always is the object (the current only > excpetion is an invocation through super()). It might help to anchor this discussion if we focus on a tangible test case. In particular, lets look at the first few lines of the first pie-thon test, which can be found here: http://svn.perl.org/parrot/trunk/languages/python/t/pie/b0.t What code should be generated for the declaration of proto___new__ or proto___repr__ functions? What code should be generated for the various assignment statements in the declaration of the method __new__ inside the MetaToken class? Particularly, the one involving a staticmethod? The conclusion I came to is that there is no way to tell in advance how a given function or method in Python is ever going to be called as there effectively is no difference between the two. - Sam Ruby
Re: Python PMC's
On Aug 23, 2005, at 22:48, Sam Ruby wrote: From December 16, 2004: http://tinyurl.com/8smmq Sounds like a really ugly misunderstanding, the more that I've proposed not to pass the object (P2 in old parlance) out of band. I've stated several times that calling conventions need changes to properly support HLLs with minor success at these times. What I've always said (and what is actually implemented now in the branch) is that methods gets the object as its first argument. A typical snippet in PIR code is now .sub __abs .param pmc self or if denoted as method: .sub __abs method # 'self' param automagically available The first argument of NCI or vtable methods is and was always SELF, which usually is the invocant and always is the object (the current only excpetion is an invocation through super()). I'm sorry if this caused confusion. - Sam Ruby leo
Re: Python PMC's
Chip Salzenberg wrote: > I apologize to Leo for accidentally making this reply to the list. > It was supposed to be private mail, but I hit 'y' just a _little_ > too soon. I had no intention of embarassing anyone. Sorry. You did, however, cause me to cancel the email I was composing. If people only respond in private to emails such as the one that you responded to, the public perception is that emails such as Leo's are acceptable. In any case, here is a shorter version of what I said in the email I cancelled: >From December 16, 2004: http://tinyurl.com/8smmq The commit message for December 17, 2004: > r7312 | rubys | 2004-12-17 22:51:48 -0500 (Fri, 17 Dec 2004) | 2 lines > > Pass 'self' as the first argument on method calls - Sam Ruby
Re: Python PMC's
I apologize to Leo for accidentally making this reply to the list. It was supposed to be private mail, but I hit 'y' just a _little_ too soon. I had no intention of embarassing anyone. Sorry. On Tue, Aug 23, 2005 at 01:04:58PM -0700, Chip Salzenberg wrote: > On Tue, Aug 23, 2005 at 09:58:21PM +0200, Leopold Toetsch wrote: > > Sam, please follow Parrot dev (or stop spreading FUD) - thanks. > > Be gentle, please. Parrot needs language developers. I'm not saying > you're right or wrong. I'm just asking you to be a little more > diplomatic. > -- > Chip Salzenberg <[EMAIL PROTECTED]> -- Chip Salzenberg <[EMAIL PROTECTED]>