
On Mon, Oct 25, 2021 at 4:24 PM Christopher Barker <pythonchb@gmail.com> wrote:
On Sun, Oct 24, 2021 at 9:58 AM Chris Angelico <rosuav@gmail.com> wrote:
def puzzle(*, a=>b+1, b=>a+1): return a, b
Aside: In a functional programming language a = b + 1 b = a + 1 would be a syntax (or at least compile time) error.
but it's a NameError in Python, yes? or maybe an UnboundLocalError, depending on context.
There are two possibilities: either it's a SyntaxError, or it's a run-time UnboundLocalError if you omit both of them (in which case it would be perfectly legal and sensible if you specify one of them).
This, indeed, would be similar to the behaviour of the current idiom we're trying to replace: In [42]: def fun(a=None, b=None): ...: a = a if a is not None else b + 1 ...: b = b if b is not None else a + 1 ...: print(f'{a=} {b=}')
similar in the sense that whether it works or not depends on how the function is called. In [45]: fun() --------------------------------------------------------------------------- TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
In [46]: fun(3) a=3 b=4
I'm currently inclined towards SyntaxError,
Except that it's not invalid syntax -- as far as syntax is concerned, you can put any valid expression in there -- it's only "bad" if it happens to use a name that is also being late bound. Is there any other place in Python where a syntax error depends on what names are used (other than keyworks, of course)?
In the first draft of the PEP, I left it underspecified, and was mentally assuming that there'd be an UnboundLocalError. Making it a SyntaxError isn't too difficult, since the compiler knows about all arguments as it's building that bytecode. It's like the checks to make sure you don't try to declare one of your parameters as 'global'. There are several reasons that I'm preferring SyntaxError here. 1) No use-case springs to mind where you would want arguments to depend on each other. 2) If, even years in the future, a use-case is found, it's much easier to make the language more permissive than less. 3) Precisely matching the semantics of "if x is None: x = <expr>" would allow right-hand references to early-bound defaults but not other late-bound defaults 4) OTOH, stating that arguments are processed left-to-right implies that *everything* about each arg is processed in that order 5) If the wrong-direction reference is a bug, it's much more helpful to get an early SyntaxError than to get UnboundLocalError later and more rarely. (BTW: I'm seeing only two options here - SyntaxError at compilation time or UnboundLocalError at call time. There's no reason to have an error at function definition time. I could be wrong though.)
I like how it closely mirrors the current idiom, and I don't think saying that it's evaluated left to right is all that complex.
But no, I can't think of a use case :-)
Yeah, it seems perfectly reasonable, but it becomes messy. Consider: def fun(a=1, b=2): print(a, b) If you change one or both of those to late-bound, it doesn't change anything. Great! Now: def fun1(a=>b + 1, b=2): print(a, b) def fun2(a=>b + 1, b=>2): print(a, b) Having b late-bound doesn't change b in any way, but it could make a bizarre difference to a's legality, depending on whether "left to right" means only late-bound defaults, or all argument processing. We've recently had a bit of a thread on python-list about the restrictions on assignment expressions, and tying in with that, the recent relaxing of restrictions on decorators. Both examples give me confidence that restricting "wrong-direction" references is correct, at least for now; if it turns out to be wrong, it can be changed. ChrisA