[Python-3000] defop ?

Talin talin at acm.org
Thu Nov 23 09:35:02 CET 2006


Guido van Rossum wrote:
> On 11/22/06, Calvin Spealman <ironfroggy at gmail.com> wrote:
>> This whole thing seems a bit off from start to finish. A seperate
>> definition syntax with a special name/expression weirdo thingy, etc.
> 
> I have the same gut feelings but find it hard to explain why.
> 
> But I've learned to trust my gut -- eventually it will come to me.

Here's my understanding of the rationale for defop:

 From an implementation standpoint, everything that defop does can be 
emulated by an appropriate set of function decorators. However, when we 
start getting into things like namespaces and operators, some of those 
decorators start to look pretty ugly and have unwanted side-effects.

If all we wanted to do was to be able to create generic functions in the 
global namespace, the decorator syntax could be fairly simple:

    @overload
    def len( x : list ):
       ...

The decorator can inspect the function object to discover the name of 
the function - so there's no need to pass a separate argument to the 
decorator telling it what function to overload:

    @overload( len )
    def len( x : list ):
       ...

This syntax is less desirable since it violates DRY.

In either case, what the decorator returns is not the reference to the 
function object, but rather the reference to the generic dispatcher 
object, 'len'. The result of the decorator will then be bound to the 
name 'len' in that scope.

(It means that for every overload, the name 'len' gets rebound to the 
same dispatcher object over and over.)

Now, all that's fine and dandy as long as we limit ourselves to only 
creating global functions with names that conform to the syntax of 
Python identifiers.

But what if we don't want to clutter up the global namespace? Suppose we 
want to create a generic function in some other namespace, such as an 
attribute of an object. Now in order to define the overload, we have to 
do something like this:

    @overload( sequtils.len )
    def len( x : list ):
       ...

The problem here is that the function object is going to get bound to 
the name 'len' no matter what - the decorator can't control that. Even 
if we go ahead and add the function object as a method of 
'sequtils.len', we still, as a side-effect, end up binding the result of 
the decorator to 'len', which will at best create a spurious symbol, and 
at worse overwrite something we actually wanted to keep.

Taking this one step further, suppose we wanted to get rid of the 
__special__ names for overloaded operators, and instead be able to use 
the actual symbol for the operator. Suppose there was a built-in 
dictionary of operators, where the keys of this dictionary was the 
actual operators themselves. So 'infix['+']' would be the expression for 
the generic infix addition operator.

You could easily create a decorator that takes the operator name as an 
argumemt, similar to @overload above:

    @overload_infix('+')
    def add_bool_to_bool( a : bool, b : bool ):
       ...

But again, you have to deal with the unwanted function name. And you 
can't embed the '+' symbol in the function name itself, since '+' isn't 
allowed in Python identifiers.

(Maybe not the best of examples, but it illustrates the idea that 
perhaps not all generic functions will have simple names.)

Now, if we had anonymous functions (hint, hint :) ) we could say 
something along the lines of:

    infix( '+' ) += def( a : bool, b: bool ):
       ...

(Not the prettiest syntax I know...but at least it gets around the 
'tyranny of naming')

I suppose you could do that with lambda now, if your implementation was 
limited to a single expression with no statements (although that's a 
question - will lambda support decorated arguments?)

So my understanding is that 'defop' is a way to get around all of these 
issues. 'defop' is nothing more than 'def', except that it has a 
different rule as to how the function object is bound to a name.

(PJE, do I have this right?)

All that being said - I'm not sure that 'defop' is the right name for 
it, or that it's even the right way to solve it. But I hope that my 
explanation (if it's correct) may help clear up the discussion a bit.

-- Talin


More information about the Python-3000 mailing list