[Python-3000] Fw: typeclasses, duck-typing

Phillip J. Eby pje at telecommunity.com
Thu May 11 18:18:39 CEST 2006


At 04:27 PM 5/11/2006 +0100, Ben.Young at risk.sungard.com wrote:
>Why not
>
>class Foo(object):
>         @specialize(arg(0))
>         def core.len(self)
>                 ...

Where does 'core' come from?  What is specialize for?  I don't understand 
what you're proposing.


>I don't know how this would appear in the class dict though? self.len()?

In my proposal, 'defop' doesn't bind anything; it's just a statement that 
defines a function object and then adds that function to the specified 
overloaded function.  If it occurs in a class body, the function would be 
*defined* where it is, but it would not be added to the targeted operation 
until after the class was created.

Doing the actual overloading would be accomplished by an "overload()" 
generic function, meaning that users could define their own generic 
function implementations and use them with 'defop'.  Decorated defops would 
apply the decorators to the function before doing the overload, so a 
RuleDispatch implementation for Py3K might use something like this to add a 
predicate:

     @when("something(arg1) and arg2>arg3")
     defop foo.bar(arg1, arg2:int, arg3:int):
          # blah blah

This assumes that 'foo.bar' is a RuleDispatchGenericFunction, and something 
like this has been defined:

     class RuleDispatchGenericFunction:
         # ...

         defop operator.overload(self, function):
             # code to extract type signature from provided function
             # along with an optional predicate tacked on by @when,
             # and then register it using RuleDispatch's indexing
             # system

In other words, I'm saying that 'defop' is equivalent to having a decorator 
that calls 'operator.overload(generic, function, cls)', and also throws 
away the function instead of binding it to a name.  (cls here is either the 
class

So by making the overload() function a generic function, we can have 
multiple implementations of generic functions.  Anybody can experiment with 
custom approaches to overloading implementations, but they can all use a 
common syntax (defop) to work.  The overload implementation for builtin 
generic functions like len() and iter() would be something like this (if it 
were expressed in Python rather than C):

     defop operator.overload(gf:BuiltinGenericType, function):
         # code to put function in the appropriate tp_* slot of the
         # declared type of the function's first argument, using
         # info from 'gf' to know which slot to use

So builtin generic functions like iter() and len() would be 
BuiltinGenericType instances, that have information that indicates what 
tp_* slot they use and what kind of C wrapper is required.  The tp_call of 
BuiltinGenericType would then just do a slot lookup for the desired 
thing.  So we end up right where we are now in terms of performance for 
these operations, because they work the same as they always did.  It's just 
that we can drop having __magic__ names for them.

In fact, we don't even change the existing C APIs that implement these 
"generic" functions; all we're really doing is splitting up the existing 
setattr/dict->tp_* slot machinery into a set of data structures to be 
driven by a single basic approach.

In other words, we're just creating a uniform way to register methods with 
generic functions, that just happens to have a special fast path for 
language-defined operations.

The most "interesting" part is likely to be the resolution of binary 
overloads for existing __add__ vs. __radd__ stuff, both in how best to 
spell such definitions in code, and in how the builtin generics would 
implement dispatching.



More information about the Python-3000 mailing list