[Python-3000] PEP 3124 - more commentary (original) (raw)
Guido van Rossum guido at python.org
Mon May 14 20:25:53 CEST 2007
- Previous message: [Python-3000] PEP 367: New Super
- Next message: [Python-3000] PEP 3124 - more commentary
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
First I'll try to explain why I don't like the sys._getframe() approach.
Phillip's current syntax is roughly:
def flatten(x): ... # this is the "base" function
@overload def flatten(y: str): ... # this adds an overloaded version
The implementation of @overload needs to use sys._getframe() to look up the name of the function ('flatten') in the surrounding namespace. I find this too fragile an approach; it means that I can't easily write another function that calls overload to get the same effect; in particular, I don't see how this code could work:
def my_overload(func): "Shorthand for @some_decorator + @overload." return some_decorator(overload(func))
@my_oveload def flatten(z: int): ...
If the overload decorator simply looked in the calling scope, it would not find 'flatten' there, since that's the local scope of my_overload. (If it devised some clever scheme of descending down the stack, I would just have to create a more complicated example.)
I find the semantics of things that use sys._getframe() muddy and would really much much rather avoid them. Using the approach in my old sandbox/overload/overloading.py code, this objection is removed: the function being overloaded is named explicitly in the decorator.
I realize that @overload is only a shorthand for @when(function). But I'd much rather not have @overload at all -- the frame inspection makes it really hard for me to explain carefully what happens without just giving the code that uses sys._getframe(); and this makes it difficult to reason about code using @overload.
My own preference for spelling this example would be
@overloadable def flatten(x): ...
@flatten.overload def _(y: str): ...
And for the combined decorator:
@my_overload(flatten) def _(z: int): ...
I also really don't like approaches based on patching the function object's code in place. Again, it makes it hard to reason about innocent-looking code. It's one thing to say "we can prove property X assuming no-one assigns a different function to my global f" (since assigning to module globals from outside the module is an extremely rare practice). It's quite another thing to say "we can prove propery X assuming no-one overloads my global f". This is why I really really really want to require flagging the overloadable function before it can be overloaded. (And that's why I propose @flatten.overload instead of @overload(flatten).)
Next, I have a question about the proceed magic argument. I can see why this is useful, and I can see why having this as a magic argument is preferable over other solutions (I couldn't come up with a better solution, and believe me I tried :-). However, I think making this the first argument would upset tools that haven't been taught about this yet. Is there any problem with making it a keyword argument with a default of None, by convention to be placed last?
Finally, I looked at the example of overloading a method instead of a function. The little dance required to overload a method defined in a base class feels fragile, and so does the magic apparently required to special-case the first argument. This is unfortunate because I imagine this to be an important use case -- I certainly would expect that the pretty-printing example would need some state that's most conveniently stored on a "pretty-printer" object where one overloads the pprint method, not a pprint function.
Forgive me if this is mentioned in the PEP, but what happens with keyword args? Can I invoke an overloaded function with (some) keyword args, assuming they match the argument names given in the default implementation? Or are we restricted to positional argument passing only? (That would be a big step backwards.)
Also, can we overload different-length signatures (like in C++ or Java)? This is very common in those languages; while Python typically uses default argument values, there are use cases that don't easily fit in that pattern (e.g. the signature of range()).
-- --Guido van Rossum (home page: http://www.python.org/~guido/)
- Previous message: [Python-3000] PEP 367: New Super
- Next message: [Python-3000] PEP 3124 - more commentary
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]