# Simplistic block scope - a syntactic sugar

**URL:** <https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952>\
**Category:** Ideas\
**Created:** [March 3, 2025, 7:55pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952 "2025-03-03T19:55:38Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 3, 2025, 7:55pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/1 "2025-03-03T19:55:38Z")

</div>

Python lacks the block scope that exists in C-like languages and is marked there by curly braces `{}`.

The common workaround is code like following:

```python
def _():
    # ...
_()

```

In which case the contents of function `_` are inside a separate block scope.

A simplistic syntactic sugar for that would be like following:

```python
block:
    # ...

```

It will be approximately equivalent to the “function as block” workaround.  
It will have the same known caveats of “first assignment is declaration” rule (such as need for `nonlocal` statement ).

Surprisingly, I cannot find any discussion about such simplistic syntactic sugar, so I decided to post it here.  
If the general idea receives some traction, I (or someone else) may be motivated to write more examples here, discuss extended syntax (to extent it to situations where “function as block” workaround isn’t applicable), make test implementation, submit PEP (that would include the rationale and review of use cases), etc.

---

<div class="post-metadata">

**Author:** ![ajoino](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/ajoino/32/6907_2.png) [@ajoino](https://discuss.python.org/u/ajoino)\
**Post date:** [March 3, 2025, 7:58pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/2 "2025-03-03T19:58:05Z")

</div>

I remember watching a presentation about blocks in Python and the speaker mentioned that the `with` statement spawned from those discussions. Maybe you could go to the PEPs that proposed similar suggestions for previous discussions?

---

<div class="post-metadata">

**Author:** ![MegaIng](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/megaing/32/16162_2.png) [@MegaIng](https://discuss.python.org/u/MegaIng)\
**Post date:** [March 3, 2025, 8:09pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/3 "2025-03-03T20:09:38Z")

</div>

- [Ad-hoc scoping to organize code](https://discuss.python.org/t/ad-hoc-scoping-to-organize-code/36601)
- [Should loops be in their own scope? [poll]](https://discuss.python.org/t/should-loops-be-in-their-own-scope-poll/10593/)
- [Lack of block scope](https://discuss.python.org/t/lack-of-block-scope/78224)

The last one has one particularly clear message at the end:

> [@Lack of block scope](https://discuss.python.org/t/lack-of-block-scope/78224/52):
>
> For moderation clarity: the final responses here explain eloquently why block scope will never happen. Further discussion is not productive.

---

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 3, 2025, 8:27pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/4 "2025-03-03T20:27:49Z")

</div>

> [@MegaIng](#):
>
> The last one has one particularly clear message at the end:
> 
> > [@](#):
> >
> > For moderation clarity: the final responses here explain eloquently why block scope will never happen. Further discussion is not productive.

I’ve seen that particular discussion (apart from several others) before posting.  
The changes to syntax discussed there (scoping rules for loops) are different from what I described here.

[PEP 359](https://peps.python.org/pep-0359/) and [Ad-hoc scoping to organize code](https://discuss.python.org/t/ad-hoc-scoping-to-organize-code/36601) is also a different idea (the “namespaces”).

---

<div class="post-metadata">

**Author:** ![nedbat](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/nedbat/32/8744_2.png) [@nedbat](https://discuss.python.org/u/nedbat)\
**Post date:** [March 3, 2025, 8:33pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/5 "2025-03-03T20:33:23Z")

</div>

The designs may be slightly different, but the reasons for proposing them (btw, you haven’t mentioned why you want block scope) and the reasons for rejecting them will likely be the same.

It can be very instructive to read past discussions and decisions before suggesting ideas that have been considered before.

---

<div class="post-metadata">

**Author:** ![MegaIng](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/megaing/32/16162_2.png) [@MegaIng](https://discuss.python.org/u/MegaIng)\
**Post date:** [March 3, 2025, 9:12pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/6 "2025-03-03T21:12:47Z")

</div>

> [@gh-user-2022](#):
>
> I’ve seen that particular discussion (apart from several others) before posting.

In your original message you said:

> [@gh-user-2022](#):
>
> Surprisingly, I cannot find any discussion about such simplistic syntactic sugar, so I decided to post it here.

If you already found these _obviously_ close related proposals, please

- link to them
- point out how your proposal is different
- argue against the points made in those threads.

Otherwise all you are doing is wasting other peoples time to retread the exact same discussions made before.

---

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 3, 2025, 10:46pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/7 "2025-03-03T22:46:43Z")

</div>

> [@nedbat](#):
>
> (btw, you haven’t mentioned why you want block scope)

Typical case when one needs block scopes inside a function is when there are several blocks of code that use some variables that are specific to them, and it’s incovenient that these **variables can “leak”** – this often leads to bugs, especially when existing code is updated.

Of course, a large function can be refactored in several ways, so as to move such blocks of code to separate function scopes. But a programming language is a practical tool, so it may be preferable to have a syntactic sugar, because developer’s time is limited (and for numerous other reasons).

**Some examples of code.**

A function that has several blocks of code with block-specific variables:

```python
def f_01_a():
    # Common state.
    a = ...
    b = ...
    ...

    # Block of code #1 with block-specific variables.
    m = ...
    n = ...
    tmp = ...
    # ... Doing something with these variables, then saving results to common state ...

    # Block of code #2 with block-specific variables.
    n = ...
    s = ...
    for i in ...:
        # Iteration-specific variables.
        r = ...
        z = ...
        tmp1 = ...
        tmp2 = ...
    # ... Doing something with these variables, then saving results to common state ...

    # Block of code #3 with block-specific variables.
    m = ...
    n = ...
    while ...:
        # Iteration-specific variables.
        i = ...
        k = ...
        tmp = ...
    # ... Doing something with these variables, then saving results to common state ...

```

It can be seen that these variables leak outside the block of code that uses them. This can lead to some hard-to-spot bugs, when e.g. a wrong variable is used in another block of code.

Typical transformation to make these blocks scoped is following:

```python
def f_01_b():
    # Common state.
    a = ...
    b = ...
    ...

    def _():
        # Block of code #1 with block-specific variable.
        m = ...
        n = ...
        tmp = ...
        # ... Doing something with these variables, then saving results to common state ...
    _()

    def _():
        # Block of code #2 with block-specific variables.
        n = ...
        s = ...
        for i in ...:
            # Iteration-specific variables.
            r = ...
            z = ...
            tmp1 = ...
            tmp2 = ...
            # ... Doing something with these variables, then saving results to common state ...
    _()

    def _():
        # Block of code #3 with block-specific variables.
        m = ...
        n = ...
        while ...:
            # Iteration-specific variables.
            i = ...
            k = ...
            tmp = ...
            # ... Doing something with these variables, then saving results to common state ...
    _()

```

When one need also to isolate varibles between iterations, it can be written like:

```python
def f_01_c():
    ...
    def _():
        # Block of code #2 with block-specific variables.
        n = ...
        s = ...
        for i in ...:
            def _():
                # Iteration-specific variables.
                r = ...
                z = ...
                tmp1 = ...
                tmp2 = ...
                # ... Doing something with these variables, then saving results to common state ...
            _()
    _()
    ...

```

With “simplistic block scope” syntactic sugar it would look like:

```python
def f_01_d():
    # Common state.
    a = ...
    b = ...
    ...

    block:
        # Block of code #1 with block-specific variable.
        m = ...
        n = ...
        tmp = ...
        # ... Doing something with these variables, then saving results to common state ...

    block:
        # Block of code #2 with block-specific variables.
        n = ...
        s = ...
        for i in ...:
            # Iteration-specific variables.
            r = ...
            z = ...
            tmp1 = ...
            tmp2 = ...
            # ... Doing something with these variables, then saving results to common state ...

    block:
        # Block of code #3 with block-specific variables.
        m = ...
        n = ...
        while ...:
            # Iteration-specific variables.
            i = ...
            k = ...
            tmp = ...
            # ... Doing something with these variables, then saving results to common state ...

```

As example of extending syntax to situation where `def _():` … `_()` is not directly applicable, the syntax could be like following, where `block:` is added after loop statement (in which case we isolate the variables between iterations):

```python
def f_01_e():
    ...
    block:
        # Block of code #2 with block-specific variables.
        n = ...
        s = ...
        for i in ...: block:
            # Iteration-specific variables.
            r = ...
            z = ...
            tmp1 = ...
            tmp2 = ...
            # ... Doing something with these variables, then saving results to common state ...
    ...

```

(Not sure whether such syntax is possible with current lexer/parser.)

**Why a syntactic sugar like `block:` may be preferable to `def _():` … `_()`?**

1. Convenience. Apart from writing less code (and making the intent clearer), it also would include better behavior when using a debugger (stepping, local varibles, etc.).
2. The syntax may be extended to the situations where `def _():` … `_()` is not applicable.
3. Performance issues with `def _():` … `_()` inside loops.
4. The syntax may be made to have different scope semantics than function-in-function. Including the requirement to use `nonlocal` statement.

---

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 3, 2025, 11:29pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/8 "2025-03-03T23:29:34Z")

</div>

> [@MegaIng](#):
>
> If you already found these _obviously_ close related proposals, please
> 
> - link to them
> - point out how your proposal is different
> - argue against the points made in those threads.
> 
> Otherwise all you are doing is wasting other peoples time to retread the exact same discussions made before.

This is a valid concern, thank you for pointing out.

Among the links you provided there are following ideas:

1. [Should loops be in their own scope? [poll]](https://discuss.python.org/t/should-loops-be-in-their-own-scope-poll/10593)  
It is different from what I posted here. It is specific to loops. It also has its own topics of discussion, e.g. making it backwards compatible.
2. [Lack of block scope](https://discuss.python.org/t/lack-of-block-scope/78224)  
This one discusses different syntax. It proposes making blocks inside constructions like `if/else`, `while/for` scoped. That specific discussion was destined to end up fruitless, because the syntax was confusing and not backwards compatible.
3. [Ad-hoc scoping to organize code](https://discuss.python.org/t/ad-hoc-scoping-to-organize-code/36601) (see also [PEP 359](https://peps.python.org/pep-0359/))  
This one is different from what I posted. Namespaces is separate from block scoping and a large topic on its own. The rationale is also different.

---

<div class="post-metadata">

**Author:** ![elis.byberi](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/elis.byberi/32/35937_2.png) [@elis.byberi](https://discuss.python.org/u/elis.byberi)\
**Post date:** [March 4, 2025, 12:29am UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/9 "2025-03-04T00:29:11Z")

</div>

> [@gh-user-2022](#):
>
> ```python
> def f_01_d():
> # Common state.
> a = ...
> b = ...
> ...
> 
> block:
> # Block of code #1 with block-specific variable.
> m = ...
> n = ...
> tmp = ...
> # ... Doing something with these variables, then saving results to common state ...
> 
> ```

How can you save results to common state variables without shadowing them?

---

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 4, 2025, 10:51am UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/10 "2025-03-04T10:51:46Z")

</div>

> [@elis.byberi](#):
>
> How can you save results to common state variables without shadowing them?

If we keep for `block:` the same scope semantics as for `def _():` … `_()`, then `nonlocal` statement would be required for assigning to variables of outer scope.

But this is the case when a different semantics may be implemented (without breaking backward compatibility). A possible rationale for having different scope semantics is to reduce confusion (as current function-in-function scope semantics are well known for confusing newcomers).

To make new scope semantics less confusing, I can think of following ideas:

1. (A less radical idea.) Emit error on shadowing. This would possibly need adding a keyword `local` opposite to `nonlocal`, to permit shadowing explicitly.
2. (A more radical idea.) JavaScript-like evolution of scoping rules: new scoping rules for variables declared with a special keyword (in JavaScript is it `let`). This can be done in backwards compatible way.

**More thoughts on the explicit variable declaration.**

In both these ideas (1) and (2) we come to consider explicit variable declaration (`local` or `let`). _(It should be noted that such evolution eventually happened in several programming languages that had the Python-like rule “first assignment is declaration”.)_ Explicit variable declaration may not be desirable in Python for ideological reasons. In the matter of backwards compatibility I think there should be no problems. Other possible concerns can be discussed.

---

<div class="post-metadata">

**Author:** ![peterc](https://avatars.discourse-cdn.com/v4/letter/p/f9ae1b/32.png) [@peterc](https://discuss.python.org/u/peterc)\
**Post date:** [March 4, 2025, 11:39am UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/11 "2025-03-04T11:39:44Z")

</div>

It’s not going to happen. I also discussed an idea similar to this a while ago.

I hope perhaps they’ll implement macros in a manner that would allow you to create blocks like this, but even that is doubtful.

In the mean time, here is a solution that works pretty well:

```python
a = 1
b = 2
class _:
    print(a, b) # 1 2
    c = 3
    b = 4
    print(a, b, c) # 1 4 3
    d = 2*c
    b = 2*d
    print(a, b, c, d) # 1 12 3 6

print(a, b) # 1 2
c = 0
d = 0

class _:
    print(a, b, c, d) # 1 2 0 0
    c = 5
c = _.c
print(a, b, c, d) # 1 2 5 0

```

actually it works much better for your desired block semantics than it did for mine.

---

<div class="post-metadata">

**Author:** ![peterc](https://avatars.discourse-cdn.com/v4/letter/p/f9ae1b/32.png) [@peterc](https://discuss.python.org/u/peterc)\
**Post date:** [March 4, 2025, 11:53am UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/12 "2025-03-04T11:53:24Z")

</div>

If python does move towards explicit declaration, I think it should be towards `let` and `let mut`. Javascript made the mistakes of having the default declaration be mutable (rather than merely shadowable) and it’s really hard to come back from that.

But I don’t see that happening before Python4. And even then I’m not sure it’d be a good idea.  
(Side note: I think it’d be cool if in Python4 we made the distinction between development/doodle code (in which the current rules apply) and a library/stable code (which would be strongly typed, and where declaring variables _might_ make sense).)

---

<div class="post-metadata">

**Author:** ![Arne](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/arne/32/4637_2.png) [@Arne](https://discuss.python.org/u/Arne)\
**Post date:** [March 4, 2025, 12:03pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/13 "2025-03-04T12:03:23Z")

</div>

> [@gh-user-2022](#):
>
> [Ad-hoc scoping to organize code](https://discuss.python.org/t/ad-hoc-scoping-to-organize-code/36601) (see also [PEP 359](https://peps.python.org/pep-0359/))  
> This one is different from what I posted. Namespaces is separate from block scoping and a large topic on its own. The rationale is also different.

Is it though? The only actual difference that I can see is that namespaces are named blocks, so you have to give each block a name. Right now you’re discussing how controlled leakage could work, and with named blocks it’s straight-forward, avoiding messy special variable declarations, leveraging existing and popular python-syntax instead:

```python
def f_01_d():

    block a:
        # Block of code "a"... could actually can omit these kinds of comments, since the block's name 
        # can just describe its purpose
        m = ...
        n = ...
        tmp = ...

    block b:
        n = a.n # for example, to get access to specific variables defined within block "a"
        s = ...
        for i in ...:
            # Iteration-specific variables.
            r = ...
            z = ...
            tmp1 = ...
            tmp2 = ...

    block c:
        m = ...
        n = b.n
        while ...:
            # Iteration-specific variables.
            i = ...
            k = ...
            tmp = ...
    return c.m

```

---

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 4, 2025, 3:07pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/14 "2025-03-04T15:07:04Z")

</div>

> [@peterc](#):
>
> If python does move towards explicit declaration, I think it should be towards `let` and `let mut`. Javascript made the mistakes of having the default declaration be mutable (rather than merely shadowable) and it’s really hard to come back from that.

Why do you think `let` + `let mut` is better than `const` + `let`?  
`const` is a more traditional keyword for that meaning, which would prevent confusion.

---

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 4, 2025, 3:27pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/15 "2025-03-04T15:27:11Z")

</div>

The “named blocks” syntax you describle extends the scope of variables to the scope of the reference to it. Which may be undesirable (because variables may not be of use outside the block, and their memory is not realeased).

> [@Arne](#):
>
> Right now you’re discussing how controlled leakage could work, and with named blocks it’s straight-forward

If we use syntax like

```python
def f():
    # Unnamed block (as posted originally).
    block:
        ...
    # Named block (as you suggested).
    block a:
        ...
    # Unnamed block, workaround using currently available syntax.
    def _():
        ...
    _()
    # Named block, workaround using currently available syntax.
    class a:
        ...

```

there is still the issue of referring to parent scope and shadowing:

```python
def f():
    a = 0
    def _():
        b = a # OK
    _()
    def _():
        a = 1 # Shadowing
    _()
    def _():
        a += 1 # Error: referenced before assignment.
    _()
    def _():
        nonlocal a
        a += 1 # OK
    _()
    def _():
        b = a # Error: referenced before assignment.
        a = 1 # Shadowing
    _()

```

The traditional way to solve the “referring to variable of parent scope” problem (that is, to have easily understood, non-confusing syntax) is to have explicit variable declarations.

However, the idea of “namespaces” you provided may let us think towards something like following:

```python
def f():
    a = 0
    def _():
        __parent_scope__.a += 1 
    _()
    # a == 1

```

That is, a syntax to directly access the parent scope.

---

<div class="post-metadata">

**Author:** ![peterc](https://avatars.discourse-cdn.com/v4/letter/p/f9ae1b/32.png) [@peterc](https://discuss.python.org/u/peterc)\
**Post date:** [March 4, 2025, 3:45pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/16 "2025-03-04T15:45:39Z")

</div>

> [@gh-user-2022](#):
>
> Why do you think `let` + `let mut` is better than `const` + `let`?  
> `const` is a more traditional keyword for that meaning, which would prevent confusion.

Because of my experience with JavaScript and Rust. People are lazy. I am too. If the mutable variant is at least as easy to type as the immutable one, people do default to using the mutable declaration. But it is super relaxed to program in a project where you know the majority of variables won’t be changed after assignment, because you can see only a minority of declarations use `let mut`.  
So IMO you should add a bit of friction to declaring mutable variables, because it makes reading code significantly nicer.

Maybe `const` + `let` could work if people use a linter that automatically converts `let` to `const`. Maybe some people do. But I only really saw `const` used for global constants, where python uses `ALL_CAPS`.

---

<div class="post-metadata">

**Author:** ![Alex-Wasowicz](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/alex-wasowicz/32/20376_2.png) [@Alex-Wasowicz](https://discuss.python.org/u/Alex-Wasowicz)\
**Post date:** [March 5, 2025, 1:36pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/17 "2025-03-05T13:36:13Z")

</div>

> [@peterc](#):
>
> > [@gh-user-2022](#):
> >
> > Why do you think `let` + `let mut` is better than `const` + `let`?
> 
> People are lazy.

Then it’s just an IDE setting. “l” key, “e” key, “enter”. People are even more lazy

---

<div class="post-metadata">

**Author:** ![gh-user-2022](https://sea2.discourse-cdn.com/flex002/user_avatar/discuss.python.org/gh-user-2022/32/26361_2.png) [@gh-user-2022](https://discuss.python.org/u/gh-user-2022)\
**Post date:** [March 5, 2025, 9:56pm UTC](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952/18 "2025-03-05T21:56:01Z")

</div>

> [@ajoino](#):
>
> I remember watching a presentation about blocks in Python and the speaker mentioned that the `with` statement spawned from those discussions. Maybe you could go to the PEPs that proposed similar suggestions for previous discussions?

I think you refer to [PEP 340 – Anonymous Block Statements | peps.python.org](https://peps.python.org/pep-0340/), which eventually evolved into [PEP 343 – The “with” Statement | peps.python.org](https://peps.python.org/pep-0343/). And previous proposals targetting the same issue were [PEP 310 – Reliable Acquisition/Release Pairs | peps.python.org](https://peps.python.org/pep-0310/) and [PEP 319 – Python Synchronize/Asynchronize Block | peps.python.org](https://peps.python.org/pep-0319/).

All these had the motivation to have a syntax for reliable release of resources. Something similar to C#'s `using`, Java’s `try-with-resources`. This was not primarily related to scope.

The motivation of [Simplistic block scope - a syntactic sugar](https://discuss.python.org/t/simplistic-block-scope-a-syntactic-sugar/82952) is to prevent leak of variables inside function (so it’s different from the motivation of `with`). And unfortunately I couldn’t find any PEPs targetting similar scoping-related issues.
