(Note: Guido's already told me off-list that he doesn't like the way this
spelling reads, but I wanted to share it anyway since it addresses one of
the recurring requests in the PEP 572 discussions for a more targeted
proposal that focused specifically on the use cases that folks had agreed
were reasonable potential use cases for inline assignment expressions.

I'll also note that another potential concern with this specific proposal
is that even though "given" wasn't used as a term in any easily discovered
Python APIs back when I first wrote PEP 3150, it's now part of the
Hypothesis testing API, so adopting it as a keyword now would be markedly
more disruptive than it might have been historically)

Recapping the use cases where the inline assignment capability received the
most agreement regarding being potentially more readable than the status
quo:

1. Making an "exactly one branch is executed" construct clearer than is the
case for nested if statements:

    if m := pattern.search(data):
        ...
    elif m := other_pattern.search(data):
        ...
    else:
        ...

2. Replacing a loop-and-a-half construct:

    while m := pattern.search(remaining_data):
        ...

3. Sharing values between filtering clauses and result expressions in
comprehensions:

    result = [(x, y, x/y) for x in data if (y := f(x))]

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". (Note:
being able to implement such a syntactic constraint is a general
consequence of using a ternary notation rather than a binary one, since it
allows the construct to start with an arbitrary expression, without
requiring that expression to be both the result of the operation *and* the
value bound to a name - it isn't unique to the "given" keyword specifically)

While the leading keyword would allow TARGET to be an arbitrary assignment
target without much chance for confusion, it could also be restricted to
simple names instead (as has been done for PEP 572.

With that spelling, the three examples above would become:

    # Exactly one branch is executed here
    if m given m = pattern.search(data):
        ...
    elif m given m = other_pattern.search(data)):
        ...
    else:
        ...

    # This name is rebound on each trip around the loop
    while m given m = pattern.search(remaining_data):
        ...

    # "f(x)" is only evaluated once on each iteration
    result = [(x, y, x/y) for x in data if y given y = f(x)]

Constraining the syntax that way (at least initially) would avoid poking
into any dark corners of Python's current scoping and expression execution
ordering semantics, while still leaving the door open to later making
"result given NAME = expr" a general purpose ternary operator that returns
the LHS, while binding the RHS to the given name as a side effect.

Using a new keyword (rather than a symbol) would make the new construct
easier to identify and search for, but also comes with all the downsides of
introducing a new keyword. (Hence the not-entirely-uncommon suggestion of
using "with" for a purpose along these lines, which runs into a different
set of problems related to trying to use "with" for two distinct and
entirely unrelated purposes).

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to