On 2 March 2018 at 12:48, Nick Coghlan email@example.com wrote:
The design intent related rationale stems from the fact that closed over references can live for an arbitrarily long time (as can regular function locals in a generator or coroutine), and I think statement locals should be as reliably ephemeral as we can reasonably make them if they're going to be different enough from function locals to be worth the hassle of adding them.
So that means asking a bunch of questions and deliberately giving the *opposite* answer for statement locals than I would for function locals:
- Visible in locals()? Yes for function locals, no for statement locals
- Visible in frame.f_locals? Yes for function locals, no for statement
- Prevents access to the same name in outer scopes? Yes for function locals,
no for statement locals
- Can be closed over? Yes for function locals, no for statement locals
Answer "no" to all those questions is only reasonable if statement local references *look* different from regular variable names. But I also think we need to proposing answering "No" to all of them to make statement locals potentially interesting enough to be worth considering.
OK, that argument makes sense to me. At least in the sense that it makes a good case for how statement-local names should work *if* we adopt them. I reserve the right to change my mind, as I haven't thought this through fully, but at least superficially I'm with you this far.
I will note that I don't see this as a compelling reason for *having* them, just a good analysis of how they might behave if we do.
Neither PEP 527 nor 3150 *needs* the syntactic markers - the compiler can figure out what is going on because it does the symbol table analysis pass before the code generation pass, and hence can tag names appropriately based on what it finds.
My concern is for people reading the code, where omitting a syntactic marker also forces *humans* to read things in two passes to make sure they understand them correctly. Consider this example:
x = 10 data = [x*x for i in range(10)]
This will give you a 10 item list, where each item is 100.
But now consider a world with statement locals, where statement locals use the same reference syntax as regular locals:
x = 10 data = [x*x for i in range(10) if (12 as x)]
That's a deliberately pathological example (and you can already write something similarly misleading using the "for x in " trick), but the fact remains that we allow out of order evaluation in expressions in a way that we don't permit for statements.
OK. That's my concern as well. But my conclusion is different - I view this as a pretty strong argument that the complexity cost is too high to justify the feature, rather than as a difficulty we need to mitigate in order to make the feature usable.
With a syntactic marker though, there's typically going to be less ambiguity about where a name comes from:
x = 10 data = [.x*.x for i in range(10) if (12 as .x)]
It's not a panacea (since you may still be shadowing a name from an outer statement), and adapting it for PEP 3150 isn't without it's problems (specifically, you need to allow ".x = 12" in the given clause to make the names match up), but it should be less confusing than allowing a new subexpression to interfere with name resolution semantics folks have been relying on for years.
On my screen right now, I can barely see the dots. That's in email with a proportional font, so not "real coding", but it's surprising (and somewhat depressing :-() to think that probably the majority of code I read these days is in emails.
So I don't think that this is a good enough fix to warrant making Tim's screen look gritty.
If statement locals behave just like function locals, then there's no reason to add them to the language in the first place - "(expr as name)" would just become a confusing second way to spell "name = expr".
Well, the whole reason this debate keeps coming up is because people keep finding places they want to type "name = expr" but they can't, because their context isn't a statement. We're basically trying to design a replacement for "name = expr" that can be used in an expression. And the status quo is "refactor your expression into multiple statements". Doing so isn't always ideal (and particularly grates on people coming from languages where assignments are expressions, like C/C++) but it is always possible.
Adding "mustn't just be another way to spell name = expr" to the requirements is an extra requirement - and arguably one that contradicts the original requirement which was "find a way to allow name = expr in expressions".
Designing a broader feature which solves the original problem is a good goal, but we need to take care not to introduce scope creep or over generalisation. In this case, IMO, we're not doing that (yet!) but we are hitting design problems which are orthogonal to the original requirement.
It's only potentially worthwhile if statement locals offer us something that function locals don't, and the clearest potential candidate for that differentiation is to provide a greater level of assurance regarding locality of use than regular name binding operations do.
I disagree. It's *still* potentially worthwhile if it just offers a clean resolution for people who want to name complex sub-parts of a comprehension. But the bar for "clean" in that case is higher, because the benefit is pretty limited. I don't think it reaches that higher bar, but I also am not sure that the benefits of the wider proposal are sufficient to warrant the amount of lowering of the bar that is needed. (That metaphor got way too strained. I hope it still made sense).
I guess I remain -1 on the proposal, and nothing that's getting said about how we can make it work is doing anything to persuade me otherwise (quite the opposite).
Yep, that's fair, as there are *lots* of viable alternatives to inline naming of subexpressions (each with various trade-offs). The question at hand is whether we can come up with semantics and syntax that folks actually like well enough to put to python-dev for a yes/no decision, with the pros and cons consolidated in one place.
And the usual proviso that "status quo wins" applies strongly here, where (as noted in Chris' PEP) there are already *many* existing solutions.