<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"Maurizio Cimadamore" <maurizio.cimadamore@oracle.com><br><b>To: </b>"John Rose" <john.r.rose@oracle.com><br><b>Cc: </b>"Archie Cobbs" <archie.cobbs@gmail.com>, "amber-dev" <amber-dev@openjdk.org>, "Liam Miller-Cushon" <cushon@google.com><br><b>Sent: </b>Wednesday, October 23, 2024 10:55:20 PM<br><b>Subject: </b>Re: JDK-8300691 - final variables in for loop headers should accept updates<br></blockquote></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;">
<blockquote cite="mid:3ACF4EBB-F749-4887-B005-7EA20A355099@oracle.com">
<blockquote>
<pre class="moz-quote-pre">…
Of course you can argue it both ways: by making the new capture treatment only available for induction variables in for loops (where the variable is not mutated in the body) we do reduce (not eliminate!) the chances of somebody observing different values for the same induction variable.
</pre>
</blockquote>
<pre class="moz-quote-pre">I don’t see the sub-point about “not eliminate”; I think the reduction is complete to the point of elimination, because of the special (weird) properties of the old-for loop.</pre>
</blockquote>
Well, there's no sub-point - I'm merely stating the (perhaos
obvious?) fact that if you allow to capture induction variable you
can set up a contrived example where you execute a lambda which
captures the _previous_ version of the induction variable. That
said, the same thing happens in for-each loops - so we're back to
the subjective judgment of whether it's ok for counted loop to have
same rules of for-each loops despite a more explicit mutation (even
if controlled).<br>
<blockquote cite="mid:3ACF4EBB-F749-4887-B005-7EA20A355099@oracle.com">
<pre class="moz-quote-pre">I also think the feature will be enjoyed far more than “20 or 30” times, or whatever corpus analysis tells us. Corpus analysis by definition measures the way people have used the language in the past, and that is often affected by their avoidance of sharp corners in the language. I believe this little polish of a central syntax in Java will affect the way people shape their code in the future. At least, it will for me, because I know I’ve done extra work to avoid the sharp corner in the past.</pre>
</blockquote>
<p>Here I don't think I agree. It's not like developers are avoiding
loop because the capture rules are what they are. If you need a
loop, you need a loop. Maybe they are avoiding capturing lambdas -
but again, replaced with what? Anonymous inner classes will have
similar issues. And if you need capture you need capture - you
can't make that go away. Perhaps they are disguising the capture a
little harder, by using an array, or an AtomicXYZ. But I'd say
that, while the numbers we have seen might surely underestimate
things a bit, I don't expect to see dramatic differences in the
wild.</p>
<p>There's another reason I think this is the case - since Java 8
there are often better ways to write loops: using the Stream API.
Of course not every loop can be rewritten using streams, but a lot
can, and developers have already done so - perhaps lowering the
number of legacy loops (of all kinds) requiring some kind of
capture.</p></blockquote><div><br></div><div>As a teacher, here is the issue most of my student struggle with, they have a code like<br></div><div><br data-mce-bogus="1"></div><div> for(var i = 0; i < 4; i++) {<br data-mce-bogus="1"></div><div> new Thread(() -> {</div><div> ...<br data-mce-bogus="1"></div><div> }).start(),<br data-mce-bogus="1"></div><div> }<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>The code can use plain threads, virtual threads, an executor or structured concurrency, etc ... you get the idea.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>Then they want to access to induction variable 'i' inside the lambda.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>At that point, some students knows how to fix the issue using another local variable in the body of the loop but others start to become "creative",<br data-mce-bogus="1"></div><div>- some are using IntStream.range().forEach(i -> ) but usually, it fails because it's quite usual for this kind of code to have also a method call that throws a checked exception (Thread.sleep(), object.wait(), thread.join(), future.get(), etc)<br data-mce-bogus="1"></div><div>- some try to write their own range() method using an "enhanced for" like Tagir said, here are the usual examples<br data-mce-bogus="1"></div><div> - for(var i : List.of(0, 1, 2, 3)) {<br data-mce-bogus="1"></div><div> - for(var i : IntStream.range(0, 4).boxed().toList()) {<br data-mce-bogus="1"></div><div> - for(var i : new int[] { 0, 1, 2, 3 }) { <br data-mce-bogus="1"></div><div> - for(var i : (Iterable<Integer>) IntStream.range(0, 4)::iterator) { // those students have read Effective Java<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>For me, allowing my students to capture the induction variable of simple for loops so they can focus on the rest of the code is a win.</div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;">
<p>Maurizio</p></blockquote><div><br></div><div>Rémi<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><p><br>
</p>
<blockquote cite="mid:3ACF4EBB-F749-4887-B005-7EA20A355099@oracle.com">
<pre class="moz-quote-pre">— John
P.S. Archie, I don’t think character positions should be appealed to; that’s not a natural tactic for the JLS. (Despite some very low-level and odd rules about forward references between field initializers, which sort of depend on character positions.) Appealing to character positions instead of block structure inhibits understanding of code at the AST level (which IDEs want to do). If I were going in that direction I’d piggy-back on the reachability analysis the JLS already has, in order to track setting of finals. I’d ask, “can the captured variable be reassigned in some code B reachable from the capture point A?” in a way analogous to asking “can this final, once assigned at point A, ever be reassigned at point B?” Note the connection to finals, which is not accidental. Finals are all about saying, “this variable is only a value, and does not provide access to a future evolution of states”.
</pre>
</blockquote><br></blockquote></div></div></body></html>