Re: PEP 671: Syntax for late-bound function argument defaults

This was probably mentioned at some point (apologies, can't afford to read the entire thread), but since the issue of left-to-right vs. early-first-then-late binding was hotly debated, I just want to point out that left-to-right better preserves scoping intuitions: foo = 42 def bar(@baz=foo, foo=1): return baz, foo With left-to-right, bar() returns (42, 1) (or whatever other value the foo global currently has). With early-first-then-late, it returns (1, 1). And according to the current version of the PEP, it's essentially undefined behavior, though AFAICS, the possibility that baz would take its value from global foo isn't even considered. As a teacher, I'd much rather teach/explain the first option, because in most of Python, you can figure out what value a name refers to by looking to the left and above of where it's used, and if you can't find it in the local scope, check globals. (One prominent place where this doesn't hold is comprehensions/genexprs, which in practice tend to be a learning bump for beginners.) As for errors -- if SyntaxError were used, then the example above would raise it, wouldn't it? Because there's no way to statically determine whether foo might also be a global, in addition to being a function parameter. So there would be no way of having a late-bound parameter take a default from a global, while having an early-bound parameter with the same name as the global. I freely acknowledge you probably won't need this most of the time, but why prohibit it outright (or why make it into undefined behavior, without even considering the global, as the PEP currently does), especially since it aligns with scoping intuitions as outlined above? Finally, as hinted by the example, I'm leaning towards the @ notation. => is pointing in the wrong direction, when programming languages use some kind of arrow-like operator for assignment, it tends to point towards the name, not the value. (I realize this is not really assignment, but the syntactic analogy is clear.) Whereas I can easily see myself telling students that the @ acts as a sort of barrier to eager evaluation/assignment of the default value (as a mnemonic). It could also be written after the =, as in baz=@foo, which reads like a quote operator and is open to future extensions where unary @ could be used to defer evaluation in more places (as some people seem to want -- I personally am wary of quote/eval/substitute shenanigans, such as R has). The barrier mnemonic could remain the same. OTOH, I like the way the prefix is more conspicuous and visually signals that the parameter is special/different right off the bat. The PEP doesn't have any examples of what the syntax for *assigning* late bound arguments is, so I'm assuming there's no special syntax? As in, bar can be called e.g. as bar(1, 2), bar(baz=1, foo=2) or bar(foo=1, baz=2)? That makes sense, but I think it's a small argument in favor of a prefix notation, so that = is kept the same across definitions and calls. Somehow, I would expect my students to be more likely to erroneously think they need to replicate => and call bar as bar(baz=>1), and less likely to write bar(@baz=1), especially with that barrier mnemonic. Just a hunch though. More seriously though, if => ever makes it into Python as the new lambda too, then bar(baz=>1) is a beginner footgun waiting to happen: you probably meant bar(baz=1), but instead, you wrote bar(baz=lambda baz: 1). Whereas bar(@baz=1) remains a harmless SyntaxError (harmless in that it fails early and gives you a good hint as to what's wrong). Best, David

On Thu, Dec 2, 2021 at 2:47 AM David Lukeš <dafydd.lukes@gmail.com> wrote:
This was probably mentioned at some point (apologies, can't afford to read the entire thread), but since the issue of left-to-right vs. early-first-then-late binding was hotly debated, I just want to point out that left-to-right better preserves scoping intuitions:
foo = 42 def bar(@baz=foo, foo=1): return baz, foo
With left-to-right, bar() returns (42, 1) (or whatever other value the foo global currently has). With early-first-then-late, it returns (1, 1). And according to the current version of the PEP, it's essentially undefined behavior, though AFAICS, the possibility that baz would take its value from global foo isn't even considered.
There are only two possible interpretations: either it uses the provided foo (or early-bound default), or it fails with UnboundLocalError. Under no circumstances would it be able to see a global. This isn't a class namespace, so the name 'foo' is always local.
As a teacher, I'd much rather teach/explain the first option, because in most of Python, you can figure out what value a name refers to by looking to the left and above of where it's used, and if you can't find it in the local scope, check globals.
(One prominent place where this doesn't hold is comprehensions/genexprs, which in practice tend to be a learning bump for beginners.)
That's an oversimplification: foo = 42 def bar(): print(foo) foo = 1 This won't print 42. But with that understanding ("locals are locals no matter where you use them"), late-bound defaults are the same as any other locals.
The PEP doesn't have any examples of what the syntax for *assigning* late bound arguments is, so I'm assuming there's no special syntax? As in, bar can be called e.g. as bar(1, 2), bar(baz=1, foo=2) or bar(foo=1, baz=2)? That makes sense, but I think it's a small argument in favor of a prefix notation, so that = is kept the same across definitions and calls. Somehow, I would expect my students to be more likely to erroneously think they need to replicate => and call bar as bar(baz=>1), and less likely to write bar(@baz=1), especially with that barrier mnemonic. Just a hunch though.
Definitely no special syntax. This makes no changes to the way functions are called, only what happens with omitted args. ChrisA
participants (2)
-
Chris Angelico
-
David Lukeš