I might not have understood it completely, but I think the use cases would probably better be splitted into two categories, each with a simple solution (simple in usage at least):

When we just want a tiny scope for a variable:

Syntax:

with [assignment]:
# use of the variable

# assigned variable is now out of scope

Examples:

with b = a + 1:
y = b + 2

# we can use y here, but not b

or

with delta = lambda a, b, c: b**2 - 4 * a * c:
x1 = (- b - math.sqrt(delta(a, b, c))) / (2 * a)
x2 = (- b + math.sqrt(delta(a, b, c))) / (2 * a)

# delta func is now out of scope and has been destroyed

We don't keep unnecessarily some variables, as well as we don't risk any collision with outer scopes (and we preserve readability by not declaring a function for that).

It would probably mean the assignment operator should behave differently than it does now which could have unexpected (to me) implications. It would have to support both __enter__ and __exit__ methods, but I don't know if this makes any sense. I don't know if with a + 1 as b: would make a better sense or be a side-effect or special cases hell.

When we want to simplify a comprehension:

(although it would probably help in many other situations)

Syntax:

prepare_iterable(sequence, *functions)

which creates a new iterable containing tuples like (element, return_of_function_1, return_of_function_2, ...)

Examples:

`[m(spam)[eggs] for _, m in prepare_iterable(sequence, lambda obj: obj[0].field.method) if m]`

or, outside of a comprehension:

sequence = [0, 1, 5]
prepare_iterable(sequence, lambda o: o * 3, lambda o: o + 1)
# -> [(0, 0, 1), (1, 3, 2), (5, 15, 6)]

The "prepare_iterable" method name might or might not be the right word to use. But English not being my mother language, I'm not the right person to discuss this...
It would be a function instead of a method shared by all iterables to be able to yield the elements instead of processing the hole set of data right from the start.
This function should probably belong to the standard library but probably not in the general namespace.

-- Brice

Le 17/06/17 à 12:27, Steven D'Aprano a écrit :
```On Sat, Jun 17, 2017 at 09:03:54AM +0200, Sven R. Kunze wrote:
```
```On 17.06.2017 02:27, Steven D'Aprano wrote:
```
```I think this is somewhat similar to a suggestion of Nick Coghlan's. One
possible syntax as a statement might be:

y = b + 2 given:
b = a + 1
```
```Just to get this right:this proposal is about reversing the order of
chaining expressions?
```
```Partly. Did you read the PEP?

https://www.python.org/dev/peps/pep-3150/

I quote:

The primary motivation is to enable a more declarative style of
programming, where the operation to be performed is presented to the
reader first, and the details of the necessary subcalculations are
presented in the following indented suite.
[...]
A secondary motivation is to simplify interim calculations in module
and class level code without polluting the resulting namespaces.

It is not *just* about reversing the order, it is also about avoiding
polluting the current namespace (global, or class) with unnecessary
temporary variables. This puts the emphasis on the important part of the
expression, not the temporary/implementation variables:

page = header + body + footer where:
body = ...
footer = ...

There is prior art: the "where" and "let" clauses in Haskell, as well as
mathematics, where it is very common to defer the definition of
temporary variables until after they are used.

```
```Instead of:

b = a + 1
c = b + 2

we could write it in reverse order:

c = b + 2 given/for:
b = a + 1
```
```
Right. But of course such a trivial example doesn't demonstrate any
benefit. This might be a better example.

Imagine you have this code, where the regular expression and the custom
sort function are used in one place only. Because they're only used
*once*, we don't really need them to be top-level global names, but
currently we have little choice.

regex = re.compile(r'.*?(\d*).*')

def custom_sort(string):
mo = regex.match(string)
... some implementation
return key

# Later
results = sorted(some_strings, key=custom_sort)

# Optional
del custom_sort, regex

Here we get the order of definitions backwards: the thing we actually
care about, results = sorted(...), is defined last, and mere
implementation details are given priority as top-level names that
either hang around forever, or need to be explicitly deleted.

Some sort of "where" clause could allow:

results = sorted(some_strings, key=custom_sort) where:
regex = re.compile(r'.*?(\d*).*')

def custom_sort(string):
mo = regex.match(string)
... some implementation
return key

If this syntax was introduced, editors would soon allow you to fold the
"where" block and hide it. The custom_sort and regex names would be
local to the where block and the results = ... line.

Another important use-case is comprehensions, where we often have to
repeat ourselves:

[obj[0].field.method(spam)[eggs] for obj in sequence if obj[0].field.method]

One work around:

[m(spam)[eggs] for m in [obj[0].field.method for obj in sequence] if m]

But perhaps we could do something like:

[m(spam)[eggs] for obj in sequence where m = obj[0].field.method if m]

or something similar.

```
```If so, I don't know if it just complicates the language with a feature
which does not save writing nor reading
```
```It helps to save reading, by pushing less-important implementation
details of an expression into an inner block where it is easy to ignore
them. Even if you don't have an editor which does code folding, it is
easy to skip over an indented block and just read the header line,
ignoring the implementation. We already do this with classes, functions,
even loops:

class K:
... implementation of K

def func(arg):
... implementation of func

for x in seq:
... implementation of loop body

page = header + body + footer where:
... implementation of page

As a general rule, any two lines at the same level of indentation are
read as being of equal importance. When we care about the implementation
details, we "drop down" into the lower indentation block. But when
skimming the code for a high-level overview, we skip the details of
indented blocks and focus only on the current level:

class K:
def func(arg):
for x in seq:
page = header + body + footer where:

(That's why editors often provide code folding, to hide the details of
an indented block. But even without that feature, we can do it in our
own head, although not as effectively.)

```