Attempting to bring things back on topic to function parameters with late binding.
On Fri, May 29, 2020 at 06:03:30PM +0200, Alex Hall wrote: > Chris was just saying that a statement like `x ?= y` meaning 'assign the > value of y to the local variable x if x is currently unbound' already fits > neatly into the language model and doesn't require major changes. Do you > disagree? > > (although I don't endorse the proposal for `x ?= y`) I have no idea whether `?=` alone requires major changes or not. Probably not, since we can already implement it as a try...except: try: x except NameError: x = y But as the proposal relates to function calls, it is a big change that will turn early detection of some errors into late detection. Consider what happens when you call a function. The interpreter matches up arguments supplied in the function call to parameters in the function signature. If any parameters don't get an argument, the intrepreter provides default values. If, after applying defaults, any parameter doesn't have a value then the intrepreter raises TypeError, otherwise it calls the function and enters the body of the function. The consequence of this is that if the function body is entered, it is impossible for any parameter to be unbound. def spam(arg): # if we enter the body of the function, then arg must be # bound and `arg ?= ...` will never apply. That means that Chris' proposed `?=` operator would not help at all to get late-binding defaults. Adding a bind-only-if-unbound operator might be useful, or not, but it doesn't solve the mutable default issue. In order to solve the mutable default issue, the intrepreter has to stop raising TypeError, and instead enter the function body with the function parameters left unbound. So exceptions like this will disappear: py> len() TypeError: len() takes exactly one argument (0 given) to be replaced with something like this: UnboundLocalError: local variable referenced before assignment when the function tries to access the unbound parameter. And we change code currently written like this: def spam(arg=None): if arg is None: arg = expression into code written like this: def spam(arg): arg ?= expression which saves one line, not much benefit to gain for the pain. So a general purpose `?` bind-if-unbound operator doesn't solve the problem at hand, even if it were useful in other ways. What if it wasn't a general purpose operator, but instead special syntax that applies only in function signatures? Then we could write: def spam(arg?=expression): and the rule for the interpreter would become: Match up arguments supplied in the function call to parameters in the function signature. If any parameters don't get an argument, apply default values: - if the default is set with `=`, it is early-bound and nothing changes from today; - if the default is set with `?=` it is late-bound and the default is re-evaluated as needed; Finally, if after applying defaults, any parameter doesn't have a value, then the intrepreter raises TypeError, otherwise it calls the function and enters the body of the function. The concept of bound-or-unbound doesn't come into this, or at least no more that it already applies to the status quo. The whole process takes place before the function is called. The only differences from the status quo are that the `?` tells: - the compiler to delay evaluating the default and store the expression itself (in some format), rather than evaluate the expression and store the evaluated value; - the interpreter to re-evaluate that expression, not just fetch the pre-evaluated value. Is that a big change or hard to implement? I don't know. -- Steven _______________________________________________ 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/PEHDA3FAFOZSD43MVI4GK24MSW4A5LQH/ Code of Conduct: http://python.org/psf/codeofconduct/