<div dir="ltr"><div dir="ltr">On Wed, Oct 16, 2024 at 8:56 AM Maurizio Cimadamore <<a href="mailto:maurizio.cimadamore@oracle.com">maurizio.cimadamore@oracle.com</a>> wrote:</div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>
<div>
<div><p style="margin:0px 0px 1.2em">An intersting angle
we discussed was whether we feel imperative for loops are
special enough to deserve special treatment, or whether this is
the start of a loosening process that will eventually lead to
allow capture for every local variable <i>value</i>.</p></div></div></blockquote><div><div>(Caveat: statements below are under the aegis of an assumed "loosening process" discussion, and is agnostic on whether a "loosening process" discussion is itself worthwhile :)<br></div><div><br></div>FWIW, it occurred to me that there might be another option worth considering on the "effectively final" spectrum that has not been mentioned (AFAIK).<br></div><div><br></div><div>The problem that "effectively final" tries to solve is the ambiguity that arises when a variable is both (a) captured by some code that will execute later (asynchronously) and yet (b) modified synchronously (i.e., outside of the capturing code).</div><div><br></div><div>The key observation here is that the problem is much more acute when the variable is modified <i>after</i> the capture.</div><div><br></div><div>For example, this:</div><div><br></div><div><span style="font-family:monospace"> int x = 42;</span></div><div><span style="font-family:monospace"> Runnable r = () -> System.out.println(x);</span></div><div><span style="font-family:monospace"> if (squareit)</span></div><div><span style="font-family:monospace"> x = x * x;<br></span></div><div><br></div><div> seems a lot worse in terms of ambiguity/confusing semantics than this:</div><div><div><br></div><div><div><span style="font-family:monospace"> int x = 42;</span></div><div><div><span style="font-family:monospace"> if (squareit)</span></div><div><span style="font-family:monospace"> x = x * x;</span><br></div><span style="font-family:monospace"> Runnable r = () -> System.out.println(x);</span></div><br></div></div><div>But wait... what exactly is the meaning of the word "after" in that observation? Does "after" mean "later in time as the program executes" or simply "lexically later in the source code of the program"?<br></div><div><br></div><div>I would claim that the definition of "after" that most closely matches programmer intuition is the latter - "lexically later in the source code of the program".</div><div><br></div><div>As an example of this, look no farther than the basic <span style="font-family:monospace">for()</span> loop we have been discussing. This example:<br></div><div><br></div><div><span style="font-family:monospace"> for (int i = 1; i <=3; i++) {</span></div><div><div><span style="font-family:monospace"> Runnable r = () -> System.out.println(i); <br></span></div></div><div><div><span style="font-family:monospace"> if (squareit)</span></div><div><span style="font-family:monospace"> i = i * i;<br></span></div><span style="font-family:monospace"></span><span style="font-family:monospace"> }<br></span></div><div><br></div><div>seems a lot worse in terms of ambiguity/confusing semantics than this one:</div><div><br></div><div><div><span style="font-family:monospace"> for (int i = 1; i <=3; i++) {</span></div><div><div><div><span style="font-family:monospace"> if (squareit)</span></div><div><span style="font-family:monospace"> i = i * i;<br></span></div><span style="font-family:monospace"> Runnable r = () -> System.out.println(i); <br></span></div></div><div><span style="font-family:monospace"> }<br></span></div></div><div><br></div><div>Even though in both cases variable <span style="font-family:monospace">i</span> is modified "later in time when the program executes".</div><div><br></div><div>Note that this would work for any type of loop, not just with for() loops, as well as plain code.<br></div><div><br></div><div>This leads to a broader possible "loosening", which would simply state: a variable can be captured by a lambda only if it is not modified at any point lexically following the lambda in the source code.<br></div><div><br></div><div>Of course, this would require replacing the term "effectively final" with... "effectively frozen"? :)<br></div><div><br></div><div>I was curious about the stats on this so I modified the <a href="https://github.com/archiecobbs/jdk/tree/dummy-variable-detector">"dummy finder"</a> to report whether or not, for each dummy variable reported, the original variable was modified after the capturing lambda.</div><div><br></div><div>Here's the results when run against the JDK:</div><div><br></div><div><span style="font-family:monospace"> 63 METHODDEF (original modified before)<br> 20 METHODDEF (original never modified)<br> 11 FORLOOP (original modified before)<br> 5 METHODDEF (original modified after)<br> 3 TRY (original modified before)<br> 3 IF (original modified before)<br> 3 DOLOOP (original modified before)<br> 2 WHILELOOP (original modified before)<br> 2 FOREACHLOOP (original never modified)<br> 1 FOREACHLOOP (original modified before)<br> 1 CASE (original modified before)</span></div><div><br></div><div>Note that out of 114 examples, there are only 5 with "original modified after": (<a href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java#L3202">example</a>). This augurs well for the usefulness of "effectively frozen".</div><div><br></div><div>The other 22 outliers are "original never modified", which means the dummy variable is unnecessary because the original is never modified. There are 2 of these in <span style="font-family:monospace">FOREACHLOOP</span>, which are probably leftovers from when for-each didn't have its special rule; the remaining 20 are in <span style="font-family:monospace">METHODEF</span> (<a href="https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ResourceBundle.java#L3225">example</a>) and these are just opportunities for small cleanups.<br></div><div><br></div><div>-Archie<br></div><br></div><span class="gmail_signature_prefix">-- </span><br><div dir="ltr" class="gmail_signature">Archie L. Cobbs<br></div></div>