[Python-ideas] PEP 572: Statement-Local Name Bindings, take three!

Chris Angelico rosuav at gmail.com
Sun Mar 25 03:18:49 EDT 2018


On Sun, Mar 25, 2018 at 4:34 PM, Guido van Rossum <guido at python.org> wrote:
> This is a super complex topic. There are at least three separate levels of
> critique possible, and all are important.

Thank you for your detailed post. I'll respond to some of it here, and
some more generally below.

> First there is the clarity of the PEP. Steven D'Aprano has given you great
> detailed feedback here and you should take it to heart (even if you disagree
> with his opinion about the specifics). I'd also recommend treating some of
> the "rejected alternatives" more like "open issues" (which are to be
> resolved during the review and feedback cycle). And you probably need some
> new terminology -- the abbreviation SNLB is awkward (I keep having to look
> it up), and I think we need a short, crisp name for the new variable type.

Agreed that it needs a new name. I've been trying to avoid looking for
something that's short-yet-inaccurate, and sticking to the
accurate-but-unwieldy; perhaps Nick's "sublocal" will serve the
purpose?

> Then there is the issue of syntax. While `(f() as x)` is a cool idea (and we
> should try to recover who deserves credit for first proposing it), it's easy
> to overlook in the middle of an exception. It's arguably more confusing
> because the scoping rules you propose are so different from the existing
> three other uses of `as NAME` -- and it causes an ugly wart in the PEP
> because two of those other uses are syntactically so close that you propose
> to ban SNLBs there. When it comes to alternatives, I think we've brainwashed
> ourselves into believing that inline assignments using `=` are evil that
> it's hard to objectively explain why it's bad -- we're just repeating the
> mantra here. I wish we could do more quantitative research into how bad this
> actually is in languages that do have it. We should also keep an open mind
> about alternative solutions present in other languages. Here it would be
> nice if we had some qualitative research into what other languages actually
> do (both about syntax and about semantics, for sure).

Not qualitative, but anecdotal: I do sometimes have to remind my
JavaScript students to check whether they've typed enough equals
signs. And that's in a language where the normal comparison operator
is ===. It's *still* not uncommon to see a comparison spelled =.

> The third issue is that of semantics. I actually see two issues here. One is
> whether we need a new scope (and whether it should be as weird as proposed).
> Steven seems to think we don't. I'm not sure that the counter-argument that
> we're already down that path with comprehension scopes is strong enough. The
> other issue is that, if we decide we *do* need (or want) statement-local
> scopes, the PEP must specify the exact scope of a name bound at any point in
> a statement. E.g. is `d[x] = (f() as x)` valid?

Yes, it is. The sublocal name (I'm going to give this term a try and
see how it works; if not, we can revert to "bullymong", err I mean
"SLNB") remains valid for all retrievals until the end of the
statement, which includes the assignment.

> And what should we do if a
> name may or may not be bound, as in `if (f(1) as x) or (f(2) as y): g(y)` --
> should that be a compile-time error (since we can easily tell that y isn't
> always defined when `g(y)` is called) or a runtime error (as we do for
> unbound "classic" locals)?

The way I've been thinking about it (and this is reflected in the
reference implementation) is that 'y' becomes, in effect, a new
variable that doesn't collide with any other 'y' in the same function
or module or anything. For the duration of this statement, 'x' and 'y'
are those special variables. So it's similar to writing this:

def func():
    x = f(1)
    if x:
        g(y)
    else:
        y = f(2)
        g(y)

which will raise UnboundLocalError when x is true. The same behaviour
happens here.

> And there are further details, e.g. are these
> really not allowed to be closures? And are they single-assignment? (Or can
> you do e.g. `(f(1) as x) + (f(2) as x)`?)

Technically, what happens is that the second one creates _another_
sublocal name, whose scope begins from the point of assignment and
goes to the end of the statement. Since this expression must all be
within one statement, both sublocals will expire simultaneously, so
it's effectively the same as reassigning to the same name, except that
the old object won't be dereferenced until the whole statement ends.
(And since Python-the-language doesn't guarantee anything about
dereferenced object destruction timings, this will just be a point of
curiosity.)

> So, there are lots of interesting questions! I do think there are somewhat
> compelling use cases; more than comprehensions (which I think are already
> over-used) I find myself frequently wishing for a better way to write
>
> m = pat.match(text)
> if m:
>     g = m.group(0)
>     if check(g):  # Some check that's not easily expressed as a regex
>         print(g)
>
> It would be nice if I could write that as
>
> if (m = pat.match(text)) and check((g = m.group(0))):
>     print(g)
>
> or
>
> if (pat.match(text) as m) and check((m.group(0) as g)):
>     print(g)
>

Agreed. I'm currently thinking that I need to do what several people
have suggested and break this into two completely separate PEPs:

1) Sublocal namespacing
2) Assignment expressions

Sublocal names can be used in a number of ways. There could be a "with
sublocal EXPR as NAME:" syntax that actually disposes of the name
binding at the end of the block, and "except Exception as e:" could
shadow rather than unbinding. Maybe list comprehensions could change,
too - instead of creating a function, they just create a sublocal
scope.

That may be the best way forward. I'm not sure.

ChrisA


More information about the Python-ideas mailing list