On Sun, Dec 12, 2021 at 12:07:30PM -0700, Carl Meyer wrote:

> 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 think you have identified a real pain point, function wrapping, but it 
is a much bigger pain point that just function defaults, and it won't be 
made appreciably worse by late-bound defaults.

Over the years, I have written about this pain point a number of times. 
It is much more significant than just defaults: it is hits parameter 
naming, and order, and annotations.

The problem is that we have no good notation to say that one function 
inherits its signature (possibly with changes) from another function.

This is especially noticable during rapid experimental development, 
where the signature of functions might be changing rapidly, but it 
occurs in stable code too. Even if the signature is changing, the 
wrapper nevertheless has to be aware of the signature, and duplicate it.

The idea that this problem is unique, or especially acute, for function 
defaults is false. It is a problem across the board:

- if the *order of parameters* changes, the wrapping function must 
  also change, unless it exclusively uses keyword arguments;

- if the *parameter names* change, the wrapping function likewise
  must also change, unless it exclusively uses positional arguments;

- if the *parameter type declarations* change, the wrapping function
  should also change, lest the reader get confused, and static or
  runtime type checkers flag the code as wrong.

Or to put it more succinctly:

The wrapper must be aware of the wrapped signature in order to duplicate 
it. Function signatures are code, and this is a violation of DRY.

If anything, the "default argument" problem is the *least* issue here, 
not the most, because the wrapper can, at least sometimes, just omit the 
parameter with a default.

The only solution to this right now is to use `*args, **kwargs`.

If that hurts introspection, then that demonstrates a weakness in our 
introspection tools that needs to be fixed.

If you try to reduce the problem by removing defaults, or annotations, 
or only using keyword arguments, or only using positional arguments, not 
only does it not solve the problem, but the solution is worse than the 
problem being solved.

But if we focus only on one tiny little corner of the problem space, 
complex defaults (whether early or late), there is one other possible 
mitigation that is out of scope for this PEP but perhaps we could 
consider it. Namely a Javascript-like undefined value that the 
interpreter can use as an explicit signal to "treat this as a missing 
argument and use the default".

But undefined has its own problems to, and its not clear to me that this 
would be any more of a solution to the tight coupling between wrapper 
and wrapped functions problem than any of the other non-solutions are.

In any case, coming back to this PEP:

- late defaults do not make the wrapper problem appreciably worse;

- the wrapper problem has something of a solution, `*args, **kwargs`,
  but our tooling needs to be better at handling that;

- that tooling weakness is independent of this PEP and should be fixed
  regardless of this PEP;

- arguably, the wrapper problem may reduce the scope for people to use 
  late defaults, since they might prefer to use None or some other
  sentinel, but it doesn't eliminate it.


-- 
Steve
_______________________________________________
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/OMK3ZOSSCCQ5MITIZUJ5TJILLZYTNOWM/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to