On 5 May 2018 at 13:36, Tim Peters
[Nick Coghlan
] ... The essence of the given clause concept would be to modify *these specific cases* (at least initially) to allow the condition expression to be followed by an inline assignment, of the form "given TARGET = EXPR".
I'm not clear on what "these specific cases" are, specifically ;-) Conditions in "if", "elif", and "while" statement expressions?
Exactly the 3 cases presented (if/elif/while conditions). The usage in comprehensions would mirror the usage in if statements, and avoid allowing name bindings in arbitrary locations due to the implicity nested scopes used by comprehensions. Conditional expressions would be initially omitted since including them would allow arbitrary name bindings in arbitrary locations via the quirky "x if x given x = expr else x" spelling, and because "else" isn't as distinctive an ending token as "given ... :", "given ... )", "given ... ]", or "given ... }".
Restricted to one "given" clause, or can they chain? In a listcomp, is it one "given" clause per "if", or after at most one "if"? Or is an "if" even needed at all in a listcomp? For example,
[(f(x)**2, f(x)**3) for x in xs]
has no conditions, and
[(fx := f(x))**2, fx**3) for x in xs]
is one reasonable use for binding expressions.
[(fx**2, fx**3) for x in xs given fx = f(x)]
reads better, although it's initially surprising (to my eyes) to find fx defined "at the end". But no more surprising than the current:
[(fx**2, fx**3) for x in xs for fx in [f(x)]]
trick.
There were a couple key reasons I left the "for x in y" case out of the initial proposal: 1. The "for x in y" header is already quite busy, especially when tuple unpacking is used in the assignment target 2. Putting the "given" clause at the end would make it ambiguous as to whether it's executed once when setting up the iterator, or on every iteration 3. You can stick in an explicit "if True" if you don't need the given variable in the filter condition [(fx**2, fx**3) for x in xs if True given fx = f(x)] And then once you've had an entire release where the filter condition was mandatory for the comprehension form, allowing the "if True" in "[(fx**2, fx**3) for x in xs given fx = f(x)]" to be implicit would be less ambiguous. [snip]
It''s certain sanest as
if x**2 + y**2 > 9 given x, y = func_returning_twople():
"given" really shines there!
Yep, that's why I don't have the same immediate reaction of "It would need to be limited to simple names as targets" reaction as I do for assignment expressions. It might still be a good restriction to start out with, though (especially if we wanted to allow multiple name bindings in a single given clause). [snip] The one-letter variable name obscures that it doesn't
actually reduce _redundancy_, though. That is, in the current
match = pattern.search(data) if match:
it's obviously less redundant typing as:
if match := pattern.search(data):
In
if match given match = pattern.search(data):
the annoying visual redundancy (& typing) persists.
Right, but that's specific to the case where the desired condition really is just "bool(target)". That's certainly likely to be a *common* use case, but if we decide that it's *that* particular flavour of redundancy that really bothers us, then there's always the "if expr as name:" spelling (similar to the way that Python had "a and b" and "a or b" logical control flow operators long before it got "a if c else b"). One more, a lovely (to my eyes) binding expression simplification
requiring two bindings in an `if` test, taken from real-life code I happened to write during the PEP discussion:
diff = x - x_base if diff: g = gcd(diff, n) if g > 1: return g
collapsed to the crisp & clear:
if (diff := x - x_base) and (g := gcd(diff, n)) > 1: return g
If only one trailing "given" clause can be given per `if` test expression, presumably I couldn't do that without trickery.
I was actually thinking that if we did want to allow multiple assignments, and we limited targets to single names, we could just use a comma as a separator: if diff and g > 1 given diff = x - x_base, g = gcd(diff, n): return g Similar to import statements, optional parentheses could be included in the grammar, allowing the name bindings to be split across multiple lines: if diff and g > 1 given ( diff = x - x_base, g = gcd(diff, n), ): return g (Other potential separators would be ";", but that reads weirdly to me since my brain expects the semi-colon to end the entire statement, and "and", but that feels overly verbose, while also being overly different from its regular meaning)
If it's more general,
if (diff given diff = x _ xbase) and g > 1 given g = gcd(diff, n):
reads worse to my eyes (perhaps because of the "visual redundancy" thing again), while
if diff and g > 1 given diff = x - x_base given g = gcd(diff, n):
has my eyes darting all over the place, and wondering which of the trailing `given` clauses executes first.
I find that last effect is lessened when using the comma as a separator within the given clause rather than repeating the keyword itself. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia