RFR: 8341782: Allow lambda capture of basic for() loop variables as with enhanced for() [v2]
Maurizio Cimadamore
mcimadamore at openjdk.org
Fri Oct 11 17:33:14 UTC 2024
On Fri, 11 Oct 2024 14:13:59 GMT, Archie Cobbs <acobbs at openjdk.org> wrote:
>>> Thanks for the bug fix suggestion. Should be fixed in [00181f7](https://github.com/openjdk/jdk/commit/00181f7f1b458ab38efc8ffee933e97f5f07419a).
>>
>> Looks good!
>
> [ Caveat: we're obviously spilling outside this bounds of this PR but hey it's an interesting discussion... ]
>
>> The feature I'm talking about (and maybe some of you may remember me talking about this before) is a re-declaration of a previously declared variable as (now) being final. Let's call it "subsequently-final":
>
> That's a neat idea - thanks for that perspective. Essentially, this would allow you to apply the `final` keyword at any point along the control flow path(s) in which a variable is visible, instead of only at its declaration. In fact, why not just reuse the `final` keyword (thus back-porting its meaning to be "Can't be reassigned from here on out")?
>
> Of course this would be a compile-time thing; as one might expect, there is a parallel runtime concept/debate around "freezing" etc.
>
> All of which points to the central importance of immutability and also therefore treading carefully to get the language concepts right.
> How do you explain to the user that if he changes the value of the variable later on, a lambda made before won't see the change, while a lambda made after will see it? Such a user will rightly think the language is keeping double copies of the variable, which a bad way to implement a variable. The way out of the dilemma, in 1.1, was to insist that only final variables can be captured (and they must be definitely assigned with their one and only value). This way you can have multiple copies under the hood, and there's no state to be shared, and the user's code will see a consistent binding for that variable.
I tend to agree with what you say here. To me though, I admit, the proposed new rules seem to push the language in the very direction that the careful design of the inner class mechanism wanted to avoid. I understand that you can think of an imperative, well-behaved loop induction variable behaving "as if" it was created fresh each time. And yet, according to the JLS, that variable is actually reassigned (case in point: you cannot make the lop variable `final`).
Overall, both the loop-only changes and your more general "subsequently-final" feature seem to defeat the very principles set out in the above: that allowing for multiple copies to be kept under the hood is confusing, and potentially surprising. For instance, the proposed changes allow this:
record Box(int x) { }
for (Box b = new Box(0) ; b.x < 10 ; b = new Box(b.x + 1)) {
Runnable r = () -> System.out.println(b);
}
}
I think users will be surprised to realize that some "old" `Box` instances might be kept alive by `Runnable` (which might leak somewhere else).
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/21415#discussion_r1797247056
More information about the compiler-dev
mailing list