
This may be a poor choice of time to wade into PEP 671 discussion, since the consensus seems to be that the thread has exhausted its usefulness. But there is one specific aspect of the discussion which (it seems to me, though I may have missed some of the hundreds of emails) has gotten inadequate discussion, and deserves better treatment in the PEP itself. On Wed, Dec 8, 2021 at 10:02 PM Chris Angelico <rosuav@gmail.com> wrote:
On Thu, Dec 9, 2021 at 3:16 PM Rob Cliffe via Python-ideas <python-ideas@python.org> wrote:
Objections to PEP 671 - Summary
...
(F) Concerns that functions using late-bound defaults were harder to wrap.
... (F) Also a matter of opinion, given that *a,**kw is the most common wrapping technique used, and will work reliably. Function signature algebra is a much larger challenge than this.
I don't think this is a fair dismissal of the concern. Taken broadly, function "wrapping" is extremely common, in the sense that what many (most?) functions do is call other functions, and there is a wide spectrum of "wrapping" from "pure wrapper that does not change the signature at all" through "non-trivial wrapper that has a different signature but requires some of the same arguments with the same semantics" all the way to "not a wrapper at all because it uses the called function for a very small portion of what it does and shares no signature with it." In any case where the same argument with the same semantics needs to be passed through multiple layers of a function call chain (and again, my experience is this is quite common in real world code; I can collect some data on this if anyone finds this assertion unconvincing), non-trivial argument defaults are painful. One is faced with two unappealing options: either duplicate the non-trivial default (in which case you have code duplication and more places to update on any change), or give up entirely on introspectable/legible signatures and use `*args, **kwargs`. I don't think it is true in real code that the latter is "the most common form of wrapping," and more importantly I think it is a poor last resort that we should not encourage. For a PEP that largely stakes its value proposition on "function signatures should be more useful," it seems pretty strange to promote `*args, **kwargs` (which makes the signature entirely useless to both humans and tooling) as an acceptable solution to the wrapping problem. This is already a problem with early-bound defaults. I think that it is a big enough problem to consider non-trivial argument defaults an anti-pattern in general, and to consider sentinels in place of non-trivial defaults to be preferable API design, rather than a hack or workaround. But late-bound defaults make the problem significantly worse, by encouraging more non-trivial argument defaults to be stuffed into signatures rather than calculated in the body, including some that can't be duplicated at all by a wrapper, therefore leaving `*args, **kwargs` the only option. So for me, any example where PEP 671 allows a non-trivial default (including e.g. `len(a)`) to be stuffed into the signature directly is a strike against PEP 671, not a use case in favor of it. This leaves the only value proposition of PEP 671 as simple mutable defaults like `=>[]` and `=>{}`. These values are trivial enough that it's not a problem to just duplicate them in wrapping cases, and I'd have no problem in principle enabling their use as argument defaults; they are effectively parallel to a default of `None` or `0` or `-1`. And I agree that the inability to do this with early-bound defaults is an unfortunate wart. But I'm not sure PEP 671 provides enough value here to justify its complexity. If we could make `=[]` and `={}` somehow "do what newbies expect" while preserving clear, predictable semantics, that would have value. But requiring it to be `=>[]` means that a newbie still has to first understand that `[]` and `{}` are special somehow and different syntax is needed to use them as defaults. It seems like this understanding is the largest barrier; once it has been crossed, is it really that much worse to do `x=None` in the signature and `x = x or []` in the body? (And perhaps improve this a bit further with PEP 505.) Carl