Jochen,

thanks for a quick answer!

> On 10. 9. 2025, at 0:57, Jochen Theodorou <[email protected]> wrote:
> On 07.09.25 15:20, Ondra Cada wrote:
> [...]
>>> because it would invalidate how local variable scoping works in general: we 
>>> do no allow shadowing of local variables, every local variable name must be 
>>> unique!
>> Quite. That's another very bad thing which should be fixed to work the way 
>> normal languages (which obviously does not include Java[*]) always did.
>> Shadowing local variables is a normal and very reasonable thing which worked 
>> perfectly and without a glitch from the Pascal up, or perhaps even Algol, I 
>> am not quite sure, it's far far ago :) Forbidding it is wrong, for it 
>> prevents e.g. copy/pasting small code snippets which just happen to contain 
>> an inner variable (most typically something like for (int i...) which just 
>> happens to be used in the code into which the snippet goes as well. The 
>> developer is then forced to change the old and well-tested code renaming the 
>> variable, which for one takes time which can be used much better elsewhere, 
>> what's worse, it brings a danger the changes would cause new bugs :(
> 
> but if the scope is lexical, then renaming the variables can be done safe.

Well, if the IDE does it by the same rules the language scoping has, then yes; 
but when done manually through regexp or so, very definitely not. Not all IDEs 
understand Groovy scoping. Mine (Xcode) does not, for one.

> And then suddenly people want to access variables that are hidden.

When they do, well, then they would rename (or use another approach).

When they do not want that — which is, in my personal experience, a vast 
majority of cases — there's no need to rename at all if the language does 
scoping and shadowing properly.

> What would be the rules?

I would simply use the well-established and decades-tested rules of C (I 
believe precisely same as Pascal and other languages have). Would there be a 
problem in some cases which C does not support at all, e.g., closures? Not sure 
of that, there might be some I did not think of.

> make the symbol the new variable till the end of the block and in all of its 
> sub blocks?

Just simply till the end of the block. Sub-blocks would see the variable unless 
they shadow it themselves, of course, but they should be unimportant.

> Like for example
> 
> def i = 10
> 10.times {def i=it+i*i; assert i == it+100;}

I believe “def i=it+i*i” should not work here. It should lead to the very same 
result as if you write “def i=i*i” in a code where there's no other i at all.

For the inner i is completely independent on the outer one.

> assert i == 10

Precisely, this one should work like that. Here we are back to the outer i, 
which was completely untouched by whatever we did with the inner one.

> or like... a declaration of a variable makes it existing from there one:

Does not sound reasonable, though of course I can be overlooking something of 
importance here.

[1]
> if (true) {
>  def i = 10 // [2]
> }
> assert i == 10

In my opinion, this should fail (compile-time for static, runtime for dynamic), 
since there's no i in this place at all.

More precisely, the [2] i does not exist anymore at this place. There might 
possibly be another i declared at [1]; if so, it will be used here.

> if (false) {
>  def i = 100
>  def j = 10
> }
> assert i == 10
> assert j == 10 // error

Again, neither i nor j exist here, unless declared at [1].

> there are too many possible rules to talk about.

I don't really think so. As always, I can be missing something of importance, 
but it seems to me that the rules are plain, completely intuitive and quite 
reasonable:

(i) a variable can be declared in any lexical scope (almost always it's a { ... 
} block, be it a function body or a block nested inside of it). About the only 
exceptions I can think of at this moment are
-- function arguments, which belong inside of the function body although 
declared outside
-- declarations inside of the for statement, which belong inside of the for 
body, although declared outside.

I might have forgot something, but I am pretty sure it would not be anything of 
great importance.

(ii) such a variable exists in the whole scope
(iii) for self-evident reasons, it is invisible and inaccessible before its 
declaration
(iv) it completely disappears at the end of that scope.

Nothing more, nothing less.

> Makes no sense if I do not know which set we are talking about now... and 
> yes... Groovy had something like the last one initially.... and it was 
> driving the people mad.

If by “the last one” you mean

if (foo) { def bar=666 /*1*/ }
assert bar==666 // the very same variable of /*1*/ still exists here!

then I bet. That's a terrible mess, to leave a variable survive its scope (well 
unless captured by a closure in that very scope — but then it survives for that 
closure only, definitely not for the outer scope/s).

>> Note also this creates another weird inconsistence — with the default it, 
>> Groovy does support shadowing all right; try e.g.,
>> ===
>> 2.times {
>>   println "outer it=$it"
>>   666.each {
>>     println "inner it=$it"
>>   }
>>   println "outer it back to $it, as it should!"
>> }
>> ===
>> I'ts completely strange and counter-intuitive that soon as I use an explicit 
>> declaration (e.g., just { int it ->, to make sure the type's right, without 
>> touching the inner code inside of the closure), I'm SOL.
> 
> I agree, it is a bit counter-intuitive, but it is practical.

Absolutely. Just the same way shadowing all the other variables would be 
practical, if available :)

> I would not say Groovy has a better design than Java. Groovy has different 
> design goals than Java.

Perhaps, but in my personal POV Groovy primarily just fixes most of the Java 
terrible howlers. Not worth to pursue this, I'd say :)

Thanks and all the best,
OC

Reply via email to