Paul Sokolovsky writes:
Also to clarify, [cowboy attitude] referred to difference in approaches in response to particular issue(s) raised. One thing is to say "it's hard to implement it better with the limited VM infrastructure and resources we have" (that of course leads to further questions/discussion), it's different thing to say to the same matter "issue exists, but it's not really an issue".
Of course those are *different*. Where I differ with you is on whether "I understand the case are describing and that you want to do something about it, but we don't consider that a problem" is "cowboy attitude".
My experience is that the development community will care when it perceives that a change serves the greater community (eg, f-strings and data classes, which are universally loved), or when a sufficiently large coalition of committers can be convinced. That's what you need to do, and accusing the core developers of cowboy attitude is not a good start IMO.
Maybe one of these years would be good time, after so many usability changes, to also tighten up theoretical side with block-level scoping.
Maybe it is. I certainly hope not, because I like Python as it is, where I rarely have to deal with declarations separate from instantiation, and code is highly dynamic without a compiler complaining, perhaps even going on strike, any time I want to do something whose semantics I know to be sound even though the compiler can't prove that from the syntax.
Yes, I know you claim that your proposed syntax won't be in my face, and quite possibly you're right with respect to the obstreperous compiler. But I also know that you make claims that are provably false, such as not making me use the syntax. True, short of XKCD 538 you can't make me write it, but if it exists somebody will make me read it.
The simplest answer is that they don't (interact with existing overdynamic features).
You say "overdynamic", I say "works for me".
They are new enough, opt-in concepts, and so can start from blank page.
You're BS-ing here -- you back off in the next sentence (quoted below). And from the community's point of view, no syntax is truly opt-in because we often work with Other People's Code. We need to read it, and often in open source we are modifying code whose style is "owned" by someone else -- if I were to work on code you maintain, I'm pretty sure you wouldn't hesitate to query "shouldn't this variable be block local" or even refuse to accept code that didn't have the block locals you think it should have.
A lot of effort and persuasion (and more or less silent acceptance of the slings and arrows from outraged dynamicists) went into the introduction of type hints. I think this will require more of the same (although maybe not: type hints are intentionally pervasive for those who use them, these declarations will presumably be occasional).
Alternatively, if debuggers, etc. are tied to locals() (vs more low-level code object introspection), could add implementation-defined support for them in locals() for simple implementations like CPython (block-local names will be mangled).
Sure, but we don't have an implementation of that support yet, do we? And adding the syntax will impose burdens on other implementations that claim to support the full language, as well as quite possibly inducing implementation differences here, unless you specify the debugger support etc. My point is that these are the kinds of complications that frequently delay acceptance of features for a release or two.
I suspect that when I'm reading other people's code I'd almost certainly read a let/const var = init_val as suite- local in such contexts, while in my own code I'd probably want to use it as statement-local a lot.
It's unclear how block-level scoping can be useful for single-statement scopes (beyond special cases like comprehensions), because well, that single statement will be assignment to such a variable (but nothing will read it). I even suspect we might imagine different things when talk about this, perhaps examples would help.
By "if statement" I mean
if foo(): pass elif bar(): pass else: pass
That is a single conditional *statement* with three suites that each might constitute an individual block or together constitute a single block (encompassing the if and elif condition expressions as well). I believe that's how the Python Language Reference uses "statement".
- We have adhoc block-scope-alikes already.
Yes, there's the problematic deletion of exception-capture variables, but I don't think the plural is justified. Comprehensions are *expressions*, not suites. Python is a *high level* language: the fact that
[x for x in y]
effectively expands to
list((x for x in y)) ,
implicitly defining a generator function and iterating that, doesn't bother me at all. It's an expression, and there's no reason for its internals to be available to the scope it occurs in, any more than v or x in
def foo(y): v =  for x in y: v.append(x) return v
should be available in the scope where foo is called. AIUI, the decision to expose x in the comprehension was so that the explanation "the comprehension above is equivalent to the suite in foo (except that no name is bound to the object denoted by 'v')" would be exact, including the leakage of x (but not v) into the containing scope. That's a pretty arbitrary reason, but ease of explanation is valued in Python, as is simplicity of implementation. I think you have to admit that given that generator expressions exist, "list((x for x in y))" is about as simple a semantics and implementation as you could ask for.
- We have on the plate cases where block scope may help.
I'll take your word for that. I don't know that I've ever run into any, and I do know that I've never thought "Damn, I wish I could use block locals here" or "I wish the author of this code had used block locals here" in Python (though I occasionally use block scope in C). Of course *I* don't matter, but the relative balance between folks with your experience and folks with mine does.
- So, convert to "real" block scoping there.
Whoa! First you have to convince the core devs that block scope really does help, and that it's the best way to improve the language's ability to express solutions to these issues. I'll trust you that it *does* address the issues. But whether it's best is entirely unclear to me. Perhaps there are are semantic tweaks like changing the intuitive explanation of comprehension from "a list comprehension is equivalent to an obvious inlined for statement" to "a list comprehension is list applied to the obvious genexp".
You don't like that particular semantic tweak because its implementation involves creating an "unnecessary" function, I know, but I do, because it unifies two similar expressions (actually, at least four), making one a trivial extension of the other.
- Don't stop there, and *let* people use explicit block-level vars if
It's "let", don't "make" people use it.
Please, that's just factually wrong. If you write it, I have to read it if I want to understand your program. If you don't intend to write code for me to read, why do we need to have the same language? Fork, and use your improved Python-like language for your private code. You'll still be able to read my code, much of which is intended for public use and visibility.
If you *really* need block-level local, perhaps just being able to use it (without rehashing half of the language) is good enough, for starters.
You continue to overstate your case. Nobody *really* *needs* block locals. Like type hints, they may be a great convenience for detecting certain kinds of logic errors, but they don't allow you to do any computation in the language you couldn't already do in the same number of lines, even the same number of tokens.
But requiring a "block" statement to create a slope in common cases like a one-armed if or for is equally inelegant (and surely more common).
But not necessarily equally costly if misunderstood, and the "always need a block statement to create a block" approach is easier to understand and explain correctly. And I disagree with you that mere verbosity is "inelegant". Concise syntax is a virtue, but so is consistent syntax. IMO, elegance lies in achieving both in the same construct. I'm not sure if elegance is possible here, consistent with existing Python syntax.
In "case a, b if ...:", if the case *doesn't* match, do you expect a and b be assigned?
Mu. I can both not expect them to be assigned, and not expect them to not be assigned, at the same time. I can learn either.
[When] you intuitively expect special scoping rules for at least the "case" line itself, [...] the block scope is the best well- proven formalism in the proglanguage theory/practice which matches that requirement.
Sure, but Python is not a Lisp. Adding arbitrary user-defined block scopes is not just a matter of one more pair of parentheses. So "best" has to be constrained by "Pythonicity", and I don't think your computer-scientific proofs can handle Pythonicity yet.