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:
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:
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:
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:
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):
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 _():
… _()
?
- 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.).
- The syntax may be extended to the situations where
def _():
… _()
is not applicable.
- Performance issues with
def _():
… _()
inside loops.
- The syntax may be made to have different scope semantics than function-in-function. Including the requirement to use
nonlocal
statement.