On Mon, Dec 06, 2021 at 03:14:36AM +1100, Chris Angelico wrote:
Closures cannot be executed without a context. Consider:
def f(x=lambda: (a:=)): if isinstance(x, FunctionType): x = x() print(a)
Here's the problem: The name 'a' should be in the context of f,
Not in the code as you show it. The a in the signature is local to the lambda, and the a in the print() call is a global. The code exactly as shown works fine, in exactly that way.
So I'm going to assume that you meant:
which compiles to a separate `lambda:(a:=)`, as per my suggestion.
but that context *does not exist* until f starts executing.
If this really cannot be solved, then we could prohibit walrus operators in late bound defaults. If something is too hard to get right, we can prohibit it until such time (if ever) that we work out how to do it. For many years, we had a restriction that you could have try...except and you could have try...finally but you couldn't have try...except... finally in a single block.
(And then we solved that technical limitation.)
Or we make them global, like they are for early bound defaults. Its not *mandatory* that walrus bindings in the default expression go into the function locals. They could go into the function's surrounding scope (usually globals). That's a perfectly acceptable solution too.
Or... be bold. Think outside the box. "Do not go where the path may lead, go instead where there is no path and leave a trail." To boldly go where no science fiction show has gone before, and other cliches.
If we can't execute the expression without the context existing, we make it exist. Details to follow in another post.
As an early-bound default, like this, it's not going to work, because the function object has to be fully constructed at function definition time.
This paragraph confused me for the longest time, because as an early-bound default, it does work. If you put `return x` after the print, you get the expected result:
a = "this is a global" f()
this is a global 
If you try to do this sort of thing with a magical function that is synthesized as part of late-bound default handling, you'll still need to figure out when f's context gets formed. Normally, it won't get formed until the stack frame for f gets constructed - which is when arguments get assigned to parameters, etc, etc. Trying to call that inner function without first giving it a context won't work.
Right. So make the context.
If you want to write up a competing reference implementation, go ahead. I don't think it will be as easy as you claim. And I'm not going to mention the possibility in the PEP *without* a reference implementation, nor do I intend to write one myself (since I dispute its usefulness).
It is the job of the PEP to describe rejected alternatives. If you choose to disregard my clearly superior suggestion *wink* but don't explain why you are rejecting it, then the PEP is not doing its job.