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
