Thank you for this, Carl. This is an excellent point, and well articulated. I agree it's important.

I also agree that this should be addressed in the PEP. If nothing else, it needs to be listed in a "Discussion" section, or similar. I think pointing to your email would be a good start.

Eric

On 12/12/2021 2:07 PM, Carl Meyer wrote:
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 <ros...@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
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JYI76DJU75T3H4NGFVLBXE5HGQBYAAW5/
Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/PH5YQ2SONZVRKZU6HHXMRXX63WUBIJQL/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to