Hello,
On Sat, 28 Nov 2020 18:59:14 -0800
Guido van Rossum
I'm definitely being nerd-sniped here, so take this with a grain of salt.
The reason why Python currently doesn't have block scopes is the rule that assignment creates or updates a variable in the closest scope. This rule has taken Python far, because it means you can create a local variable by assigning to it without having to declare it.
And everybody loves Python for that. Nothing beats Python in being able to take a 15-line algorithm in pseudocode, and write it in a way that it still looks like a pseudocode but also actually runs. But there're also 100-line and 200-line algorithms. And Python isn't used just as a "pseudocode", but as a real implementation language for software systems of different scales. And such usage over long time, identified some, well, weaknesses. Some of them were addressed, but arguably, in somewhat adhoc ways. And now there's an idea that maybe it's time to address even wider scope of issues in a more general way.
If we wanted to introduce something similar to C's block scopes, regardless of syntax, we'd have to answer the question where local variables will be declared.
Perhaps, we can start with saying "there won't be any breakages to already existing scoping rules" (well, maybe rough-edge cleanups for cases where adhoc workarounds were already applied).
Suppose we had the (crummy) syntax
block var1 = exp1, var2 = exp2, ...: <statements>
as the moral equivalent to C's
{ int var1 = exp1, var2 = exp2, ...; <statements> }
then we'd still have to decide what should happen to assignments to variables *other* than var1, var2, etc.
Simple answer: nothing. They will work as before.
Consider
def f(x): block a = 1: x = a # Is this local to the block or to the function? return x
print(f(0)) # 0 or 1?
IMO, if 'x = a' were to create a new variable in the block, I think that would be confusing.
Not just "confusing", it would break backward compatibility. So, no go.
It would also beg the question why we bother to adorn the block with variable declarations.
Indeed! So, we would take hint from existing Python variable declarations, like "global" and "nonlocal", and would write it like (let's imagine for a moment that we use "let" to introduce block-local *mutable* variables): def f(x): if 1: # We can bikeshed how to make this more "beautiful" later. let a = 1 x = a # Is this local to the block or to the function? return x
There are other good reasons why any variable *not* explicitly declared as being part of a block should have function scope:
Backward compatibility is at the top of them, I'm sure.
otherwise it would be impossible for code in a block to have a side effect to be consumed by the code outside the block. That could be addressed by using nonlocal, but at that point we might as well use a nested class statement (at least that gives us a way to get the locals out, as attributes of said class). Certainly it would be a shame if we added a block scope concept and we couldn't upgrade the for-loop to make use of it, optionally (like you can do in C with "for (int i = 0; i < n; i++) { ... }"). And a for-loop that can't set other variables that survive the loop seems crippled.
So maybe we should go with Paul's original gut feelings and introduce
let var1 = exp1, var2 = exp2, ...: <statements>
with the semantics that it doesn't really create a fully-fledged new scope but defines scoped constants.
... except not constants, by *mutable* variables, ("const" would introduce constants). And making it a suite-starting statement also seems like bowing too much at the side of the functional languages (I mean, typical syntax used for let's in them). "let", "const", are really look similar to "global" and "const", except that they allow an assignment to follow. (It's debatable whether multiple vars per one "let" should be allowed.)
It then would make sense to add some other syntax extensions like
for let i in ...: <statements>
for const i in ...:
(makes i local to the loop, giving it the semantics needed by closures as well) and
with contextmanager() as let x: <statements>
Likely also const.
Clearly we need to bikeshed more about the syntax.
We could then declare that comprehensions implicitly use 'for let ...'. If we were to drive this through quickly enough we could even make it apply to pattern matching captures. (Or maybe pattern matching could get these semantics anyway.)
My bus is here, so that's it,
Thanks!
-- --Guido van Rossum (python.org/~guido)
-- Best regards, Paul mailto:pmiscml@gmail.com