On Fri, Nov 27, 2020 at 08:32:04AM +1100, Cameron Simpson wrote:
> On 27Nov2020 00:25, Steven D'Aprano <st...@pearwood.info> wrote:
> >Block scoping allows shadowing within a function.
> 
> Just to this: it needn't.

Yes, I'm aware of that, and discussed languages such as Java which 
prohibit name shadowing within a function.

https://docs.oracle.com/javase/specs/jls/se7/html/jls-6.html#jls-6.4

Shadowing is a double-edged sword. It is sometimes useful, but often a 
source of hard to find bugs. One might argue that a language should 
allow *some* shadowing but not too much.


> You could forbid shadowing of the _static_ outer scope easily enough at 
> parse/compile time. That would prevent a certain class of easy misuse.
> 
>     i = 9
>     { new scope here for "i" ==> parse/compile error, since "i" is in 
>     play
>     }

Yes, that's precisely the sort of thing I discussed, and described as 
action-at-a-distance between two scopes, where the mere existence of a 
name in scope A prevents you from using the same name in scope B.

The problem is, if your inner block scope must not reuse variable names 
in the outer function scope, well, what's the advantage to making them 
seperate scopes?

Analogy: I think most of us would consider it *really weird* if this 
code was prohibited:

    a = None

    def func():
        a = 1  # Local with the same name as the global prohibited.

True, it might save newbies who haven't learned about the global keyword 
from making a few mistakes, but that's a pretty small, and transient, 
benefit. (Most coders are newbies for, what, one or two percent of 
their active coding life?)

One possible advantage, I guess, is that if your language only runs the 
garbage collector when leaving a scope, adding extra scopes helps to 
encourage the timely collection of garbage. I don't think that's a big 
advantage to CPython with it's reference counting gc.


> That said, there _are_ times I wish I could mark out the lifetime of a 
> variable, akin to C level:
> 
>     ... i does not exist ...
>     { int i;
>       ... use i ...
>     }
>     ... i now unknown, use is an error ...
>
> The nearest Python equivalent is:
> 
>     i = blah()
>     ... use i
>     del i
> 
> which feels fragile - accidental assignment to "i" later is not 
> forbidden.

Why do you care about the *name* "i" rather than whatever value is bound 
to that name?

I completely get the idea of caring about the lifetime of an object, 
e.g. I understand the need to garbage collect the exception object when 
leaving `except` blocks. (At least by default.)

But I don't get why I might care about the lifetime of a *name*.


    try:
        ...
    except Exception as e:
        pass
    e  # Name is unbound, for good reasons.
    e = None  # But why should this be an error?


We don't generally take the position that reuse of a name in the same 
function is Considered Harmful, let alone *so harmful* that we need the 
compiler to protect us from doing so.

If I am *accidentally* reusing names, my code has much bigger 
problems than just the name re-use:

- my names are so generic and undescriptive that that can be re-used for 
unrelated purposes;

- and the function is so large and/or complicated that I don't notice 
when I am re-using a name.

Name re-use in the bad sense is a symptom of poor code, not a cause of 
it, and as such block scopes are covering up the problem.

(That's my opinionated opinion :-)


-- 
Steve
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/X3DCRCNH52KVWCOOPJNVI2RHJRCK3LI2/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to