<div dir="ltr"><div>[note: I'm a new Oracle/JPG member, though this is coming from my personal mail. I'm not speaking for the team though, just myself.]</div><div><br></div>Thanks very much for all your work on this, and your whole approach to it all.<div><br></div><div>It is pretty easy to classify this as a "quality-of-life" improvement, and I recall that it was very well-received on reddit. Since its main effect is to let something work that otherwise would not work, and would force busywork on the poor user, I think we'd be unlikely to get complaints about it!</div><div><br></div><div>That said, I'd like to try to summarize the best argument against the change (that I know of) anyway. To be clear, I don't think the team currently views this argument as outweighing the benefits, but I wanted to put it out there in case it sparks any further realizations out there. I also apologize that it's not short!</div><div><br></div><div>A little background first:</div><div><br></div><div>What exactly gets captured when a lambda captures something? It has always been of a <i>value</i>, never a whole variable. However, there's a trick, because there is an invisible `this.` in front of any <i>field</i> mentioned, and it is <i>that</i> value that is captured. This creates the illusion that maybe it really is a variable being captured. So, to the user it might feel like there are two different rules in effect for two different kinds of variables.</div><div><br></div><div>Fortunately, the protections against capturing a non-effectively-final local variable prevent that distinction from mattering! So users don't have to be troubled by this quirk.</div><div><br></div><div>The enhanced for loop steps right up to the edge of this line. It seems like <i>one</i> variable that is changing, right? But our allowing it to be marked `final` is a statement that no, it really is a fresh variable each time through the loop. And I claim that was sensible: there is nothing you can do to that variable inside your loop body that has any chance of affecting its value on the next trip through the loop. So it is <i>cleaner</i> to conceptualize this as a new variable every time (as if its declaration had been inside the braces).</div><div><br></div><div>All that is <i>not</i> the case for the classic for loop. Since the update clause in the header is very often (usually!) an increment statement, your changes in the body can certainly affect the next iteration. Obviously your proposal excludes this case, as it must, but I claim that the <i>general</i> fact remains: that to understand classic for loops we must be mindful that it really is just one reused variable we're looking at.</div><div><br></div><div>None of this actually sinks your proposal by itself! But it does mean that I would characterize your proposal as creating a special case: we feel it's okay to allow it because we find it sufficiently <i>implausible</i> that the user meant to capture the variable.</div><div><br></div><div>My concern is that now there will be some code out there that <i>appears</i> to proclaim to the reader "hey, it's values, not variables, that get captured". (Ironically, <i>it is!</i>, ha ha, so you'd think this is fine. But as discussed a moment ago, the point is that today it's a "distinction without a difference" and it wouldn't be anymore.)</div><div><br></div><div>I do fully get that not being able to refer to a loop variable in a lambda is plain annoying. The next time I hit this in my own coding I will feel as annoyed as anyone. :-) For what it's worth I see it as "just annoyance", though, which has only a self-contained incremental cost each time you hit it. I find it easy to accept costs like that, compared to the more unknowable costs of opening up cracks in the user's clean and simple conceptual model of how the language works (but that's me and my nature).</div><div><br></div><div>And I think that a reasonable reaction to that annoyance is "oh, right, this is annoying, but at least it makes sense: this <i>is</i> a mutating variable, and Java doesn't allow those across the board because they create ambiguity." It at least doesn't teach a wrong lesson.</div><div><br></div><div>[Aside from all that, the second issue has been "are we sure this is really the *one* exception to the usual rule that we need, and then we're done?", but I don't have anything new to say about that.]</div><div><br></div><div>Having spelled that out, it is only for everyone's consideration. If the benefits are considered to outweigh it, then we should go ahead and I'll still be a happy camper. </div><div><br></div><div><br></div></div><br><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Wed, Dec 11, 2024 at 12:25 PM Archie Cobbs <<a href="mailto:archie.cobbs@gmail.com">archie.cobbs@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>Dear Spec Experts,</div><div><br></div><div>There is a (small) language change under consideration on amber-dev, and opinions have been expressed in multiple directions, so I'm turning to this list for hopefully some authoritative adjudication.</div><div><div><div><br></div></div><div>The idea is: allow loop variables declared in basic (old
style) for() loops to be captured in the body of the loop as long as
they are not reassigned in the body of the loop, even though they might be
reassigned in the loop step:</div><div style="margin-left:40px"><pre><span>for</span> (<span>int</span> <span>i</span> = <span>1</span>; <span>i</span> <= <span>3</span>; <span>i</span>++) {
<span>Runnable</span> <span>r</span> = () -> <span>System</span>.<span>out</span>.<span>println</span>(i); // allow this
}</pre></div></div><div>Long discussion starts here (continues into October): <a href="https://mail.openjdk.org/pipermail/amber-dev/2024-September/008921.html" target="_blank">https://mail.openjdk.org/pipermail/amber-dev/2024-September/008921.html</a></div><div><br><div>Here's what has been drafted so far (all subject to improvement of course):<br></div><div><div><div id="m_-2752655341622297208gmail-q_4765" aria-label="Hide expanded content" aria-expanded="true"><div></div></div></div><div><div><br></div><div>Bug: <a href="https://bugs.openjdk.org/browse/JDK-8341782" target="_blank">https://bugs.openjdk.org/browse/JDK-8341782</a></div><div>CSR: <a href="https://bugs.openjdk.org/browse/JDK-8341783" target="_blank">https://bugs.openjdk.org/browse/JDK-8341783</a><br>JEP: <a href="https://bugs.openjdk.org/browse/JDK-8341785" target="_blank">https://bugs.openjdk.org/browse/JDK-8341785</a><br>JLS: <a href="https://bugs.openjdk.org/browse/JDK-8341786" target="_blank">https://bugs.openjdk.org/browse/JDK-8341786</a><br>PR: <a href="https://github.com/openjdk/jdk/pull/21415" target="_blank">https://github.com/openjdk/jdk/pull/21415</a></div><br></div></div></div><div>Just to be clear, I know it looks like I've got a lot personally invested in this, but honestly I don't care about the outcome. Yes I think this would be a nice tweak, but it's not my decision. Instead, the items listed above are there to create a tangible basis for making such a decision. I am really just trying to knock this item off my to-do list one way or the other (scrubbing the to-do list is a favorite year-end activity).<br></div><div><div><br></div><div>Let me know your collective thoughts, ideally one of: ▢ Proceed ▢ Reject ▢ Shelve it for later<br></div><div><br></div></div><div>Thanks!</div><div>-Archie</div><div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature">Archie L. Cobbs<br></div><div><br></div></div>
</blockquote></div>