On Mon, 30 Nov 2020 14:45:26 +0900 "Stephen J. Turnbull" firstname.lastname@example.org wrote:
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".
Right, all people are different. That's great!
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.
Sorry, but there may be a suggestion of tactics of sneaking somebody's "pet feature" past the attention of core developers by employing sycophant techniques. That's certainly not me. Let the criticism begin. Both of "doing it" and "not doing it".
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,
But then you've already lost, because today's CPython (3.9) is not like that of a year ago, and that one is not like that of 2 years ago, and it all has been changing at alarming pace. So, calling for "stop the world" right now seems to be a bit ungrounded, if anything, newer changes proposed take inspiration in the changes already done.
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.
Good news is that it all stays. But if you want all that, perhaps you appreciate that some other people may want different things too. Like, more speed (JIT), better scalability to complex codebases, tighter formal side (syntax/semantics), etc.
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.
Ok, it will be it will be a fun cute read, calling for existing memories (or for youngest of us actually teaching them useful things, which apply outside Python too).
The simplest answer is that they don't (interact with existing overdynamic features).
You say "overdynamic", I say "works for me".
... And some say "slow".
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.
But if you anticipate that there may be people who will use block-local vars in Python "when they should have been", don't you think that "saving" them from that by simply not letting them hold of block-scoped vars is a bit ... umm, backward way to tackle the issue?
Do you think it would be worse than that with us? I don't.
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).
And block scope is initially introduce as internal compilation pipeline feature at all, but with an immediate notice: it's not intended to be hidden forever, many other languages offer it as "public API", so let's be getting used to "measure up" it for Python too.
And strangely, this latter notice is getting more (surface) attention than the initial idea to resolve corner semantic cases for existing or to-be-added constructs.
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?
Only idea and a quick prototype, which still allows to get some "feel" of what it may be like.
And adding the syntax will impose burdens on other implementations that claim to support the full language,
Oh, so again, it's a bit too late to get conscientious in that regard, after stuffing f-strings and walrus operator into the language, shaking up dictionaries from ol' good "mapping" computer-science data structure into some adhoc, CPython-specific little monster, etc.
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.
The claim was that block scoping is not a rocket science, but baseline compiler science. The underlying machinery is not complex (it's variable name rewriting, that's all). Surely implementation always has its own side, but overall, this feature should be a smaller (but "deeper") change than many others.
Aspects of debugger support is not part of the language (syntax/semantics), but an implementation detail of a particular implementation. Certainly, if debugger support is important for such one, it will be addressed.
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).
Right. And more specifically, this "if" statement may be a block with other statements (at the same level), with which it shares block scope.
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.
But "adhoc" is not a swear-word. It means that each case was addressed with its own approach, each somewhat (or quite) different. What's proposed instead is a common way to address them, a couple of more cases "on the plate", and hopefully, some of the future challenges too.
Comprehensions are *expressions*, not suites.
Yes. But expression can be used as a statement. And block/suite is defined as "one or more statements". That's how we can apply machinery for "block scope" to expressions.
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.
That's good, because how it's actually implemented in the code is implementation detail, which should bother people working on the compiler. And block scoping is initially introduced exactly to address hurdles the compiler has with implementing some language features. (Where "hurdles" include "being less optimal than could be".)
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
From the point of view of lambda calculus, function abstraction, application, and alpha/beta conversion all go in the same pack, so what you write is definitely right ;-). Implementation-wise however, some constructs are less efficient than other, e.g. functions are almost often heavier-weight than expressions/statements (even in machine code). That's why a baseline compiler optimization is inlining. Here we vice-versa, "outline" a comprehension expression to a function. That can be improved upon.
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.
Absolutely right. Simple implementation. But not the most efficient one. And with block-scoping infrastructure available, avoiding function overhead would be almost as easy, but tad more efficient in the end.
But most importantly, a common infrastructure will be reused to address different cases, and that's the main motivation here.
- We have on the plate cases where block scope may help.
I'll take your word for that.
One case is "undefined behavior" in case of not-matching with pattern matching, another case is peculiarities with "for" loop variable closure-capturing, on python-idea. Again, how block scoping helps there is that the same proven, generic (thus easy(er) to explain) machinery would be used. It's not a "great enabler" or something. Alternative solutions exist. Quite adhoc and/or more complex/less efficient IMHO.
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
But that's good, isn't it? If anything, that would be a reason to "keep calm and not worrying", isn't it? There's no conspiracy going on to put everyone in an uncomfortable position of realizing that they "did it all wrong all this time", and all their variables should have been "new variables on the block". That's totally not the case.
(though I occasionally use block scope in C).
Oh, so we're on that track already, right? It's just that we got used to think that "Python is special". Because we've been told that so many times. But after peering into it for enough time, it seems that Python is much less of that special snowflake it's drawn as sometimes. Well, that's how I arrived here. So, please hold on to that path.
Of course *I* don't matter, but the relative balance between folks with your experience and folks with mine does.
Yep, that's why we run discussions, trying to show insights we've got to other people, to check if they see something similar in there.
- 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
Well, but you might be suggesting that I'm a kind of titan on whose shoulders lies the task of conveying some sacred knowledge to "the core devs". If it's like that, "all is lost" already (for a few years, though some things in Python history were actually lost for decades).
First, "my idea" is not mine. Block scoping is again a well-known feature used in many other languages. I just once saw and was amazed by seemingly right-on-the-surface idea to apply it to Python needs, and try to spread gospel of this revelation.
In all fairness, the best outcome could be now is that some of the core devs says something like: "Yeah, I wanted to try that idea several years ago. But then I went on Hawaii vacation for a month (much better spend of time than this stuff), and on return, started renovation in the house, that's how I forgot about it."
Otherwise I fully understand it will be a long thorny path. But somebody should start it.
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
But I absolutely like it! I just don't sit with blissful smile on my face contemplating the object of my liking, but use it as motivation to rise up and say: "That's nice, but we can do better!" (Some other things I actually don't like, e.g. "except as e" hack).
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
But you overstate your worry about the block scopes, and than apparently I overstate my comforting those worries.
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.
Here you respond to your own quote, and I'm losing track of what arguments apply to. So, no response from me.
[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, we want be adding Python's analog of parentheses, which is indented statement suite. Because well, that's not a cheap as a pair of parent in LISP, so *unconditionally requiring* new indented block just for block scope vars is too much. We'll use existing intended blocks. This follows ideas how it's done in most statement-based languages (except that many use braces instead of indentation).
So "best" has to be constrained by "Pythonicity", and I don't think your computer-scientific proofs can handle Pythonicity yet.
You know, "Pythonicity" term lost much of its charm over the very last few years. You started your previous mail with mentioning a case of f-strings, saying that "people waited for string interpolation since Python 1.5". I was there at that time, and remember *quite different* story. I remember regular sabotage spies from the Perl-land trying to seed confusion in the happy Python-island. But those were detected quickly and their harmful influence were humbly but with a firm hand rejected. Because explicit is better than an implicit. Because there's one way to do it. And if you have one way to say explicitly that you format a string with '"%s" % foo', why would there would be a need to hide formatting inside strings themselves and confuse the innocent?
The suddenly, .format()! And f-strings were a fatal blow to the Pythonicity As We Knew It.
With that introduction in mind, the solution I advocate is exactly Pythonic, trust me. It's based on the idea that there's no need to hamper readability and learnability by introducing heavy-weigth indented blocks without good need. Instead, it reuses existing practice of "variable annotations keywords" like "global" and "nonlocal".
In this way, it *differs* from the tradition of LISP and other functional languages, which, due to their expression (vs statement) orientation, indeed usually have a dedicated form for lexical scoping:
(using ML-like syntax)
let var1 = expr1, var2 = expr2 ... in expr
Which is itself an expression, so you can write:
(let x = 10 in x) + 1
We don't need all that (well, we don't let most of that), and thus we don't need to burden ourselves with "let" being a block statement.