[Python-3000] New section for PEP 3124 (original) (raw)

Guido van Rossum guido at python.org
Wed Jul 25 00:16:46 CEST 2007


I'm confused why you spend so much time refuting the argument, given that you've already agreed to implement explicit decoration. Did I misread that? As I tried to indicate with my "gut feelings" argument this is not something that's up to rational argument. Also, the paragraph starting with "As a result, the vast majority of overloads can be found adjacent to..." sounds like it isn't a big loss to require explicit decoration. So I'm sticking with it.

On 7/24/07, Phillip J. Eby <pje at telecommunity.com> wrote:

Taking the recent threads here, and Guido's comments off-list, I've attempted to put together a coherent response as a new section for the PEP, which I've checked in and included a copy of here. If I have misrepresented anyone's argument, or if you spot something where you have a question or need a clarification, please let me know. Thanks.

Overloading Usage Patterns ========================== In discussion on the Python-3000 list, the proposed feature of allowing arbitrary functions to be overloaded has been somewhat controversial, with some people expressing concern that this would make programs more difficult to understand. The general thrust of this argument is that one cannot rely on what a function does, if it can be changed from anywhere in the program at any time. Even though in principle this can already happen through monkeypatching or code substitution, it is considered poor practice to do so. However, providing support for overloading any function (or so the argument goes), is implicitly blessing such changes as being an acceptable practice. This argument appears to make sense in theory, but it is almost entirely mooted in practice for two reasons. First, people are generally not perverse, defining a function to do one thing in one place, and then summarily defining it to do the opposite somewhere else! The principal reasons to extend the behavior of a function that has not been specifically made generic are to: * Add special cases not contemplated by the original function's author, such as support for additional types. * Be notified of an action in order to cause some related operation to be performed, either before the original operation is performed, after it, or both. This can include general-purpose operations like adding logging, timing, or tracing, as well as application-specific behavior. None of these reasons for adding overloads imply any change to the intended default or overall behavior of the existing function, however. Just as a base class method may be overridden by a subclass for these same two reasons, so too may a function be overloaded to provide for such enhancements. In other words, universal overloading does not equal arbitrary overloading, in the sense that we need not expect people to randomly redefine the behavior of existing functions in illogical or unpredictable ways. If they did so, it would be no less of a bad practice than any other way of writing illogical or unpredictable code! However, to distinguish bad practice from good, it is perhaps necessary to clarify further what good practice for defining overloads is. And that brings us to the second reason why generic functions do not necessarily make programs harder to understand: overloading patterns in actual programs tend to follow very predictable patterns. (Both in Python and in languages that have no non-generic functions.) If a module is defining a new generic operation, it will usually also define any required overloads for existing types in the same place. Likewise, if a module is defining a new type, then it will usually define overloads there for any generic functions that it knows or cares about. As a result, the vast majority of overloads can be found adjacent to either the function being overloaded, or to a newly-defined type for which the overload is adding support. Thus, overloads are highly- discoverable in the common case, as you are either looking at the function or the type, or both. It is only in rather infrequent cases that one will have overloads in a module that contains neither the function nor the type(s) for which the overload is added. This would be the case if, say, a third-party created a bridge of support between one library's types and another library's generic function(s). In such a case, however, best practice suggests prominently advertising this, especially by way of the module name. For example, PyProtocols defines such bridge support for working with Zope interfaces and legacy Twisted interfaces, using modules called protocols.twistedsupport and protocols.zopesupport. (These bridges are done with interface adapters, rather than generic functions, but the basic principle is the same.) In short, understanding programs in the presence of universal overloading need not be any more difficult, given that the vast majority of overloads will either be adjacent to a function, or the definition of a type that is passed to that function. And, in the absence of incompetence or deliberate intention to be obscure, the few overloads that are not adjacent to the relevant type(s) or function(s), will generally not need to be understood or known about outside the scope where those overloads are defined. (Except in the "support modules" case, where best practice suggests naming them accordingly.)


Python-3000 mailing list Python-3000 at python.org http://mail.python.org/mailman/listinfo/python-3000 Unsubscribe: http://mail.python.org/mailman/options/python-3000/guido%40python.org

-- --Guido van Rossum (home page: http://www.python.org/~guido/)



More information about the Python-3000 mailing list