JDK-8300691 - final variables in for loop headers should accept updates
Archie Cobbs
archie.cobbs at gmail.com
Sat Oct 19 17:14:12 UTC 2024
On Wed, Oct 16, 2024 at 8:56 AM Maurizio Cimadamore <
maurizio.cimadamore at oracle.com> wrote:
> 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 *value*.
>
(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 :)
FWIW, it occurred to me that there might be another option worth
considering on the "effectively final" spectrum that has not been mentioned
(AFAIK).
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).
The key observation here is that the problem is much more acute when the
variable is modified *after* the capture.
For example, this:
int x = 42;
Runnable r = () -> System.out.println(x);
if (squareit)
x = x * x;
seems a lot worse in terms of ambiguity/confusing semantics than this:
int x = 42;
if (squareit)
x = x * x;
Runnable r = () -> System.out.println(x);
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"?
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".
As an example of this, look no farther than the basic for() loop we have
been discussing. This example:
for (int i = 1; i <=3; i++) {
Runnable r = () -> System.out.println(i);
if (squareit)
i = i * i;
}
seems a lot worse in terms of ambiguity/confusing semantics than this one:
for (int i = 1; i <=3; i++) {
if (squareit)
i = i * i;
Runnable r = () -> System.out.println(i);
}
Even though in both cases variable i is modified "later in time when the
program executes".
Note that this would work for any type of loop, not just with for() loops,
as well as plain code.
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.
Of course, this would require replacing the term "effectively final"
with... "effectively frozen"? :)
I was curious about the stats on this so I modified the "dummy finder"
<https://github.com/archiecobbs/jdk/tree/dummy-variable-detector> to report
whether or not, for each dummy variable reported, the original variable was
modified after the capturing lambda.
Here's the results when run against the JDK:
63 METHODDEF (original modified before)
20 METHODDEF (original never modified)
11 FORLOOP (original modified before)
5 METHODDEF (original modified after)
3 TRY (original modified before)
3 IF (original modified before)
3 DOLOOP (original modified before)
2 WHILELOOP (original modified before)
2 FOREACHLOOP (original never modified)
1 FOREACHLOOP (original modified before)
1 CASE (original modified before)
Note that out of 114 examples, there are only 5 with "original modified
after": (example
<https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/time/format/DateTimeFormatterBuilder.java#L3202>).
This augurs well for the usefulness of "effectively frozen".
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 FOREACHLOOP, which are probably leftovers from when for-each
didn't have its special rule; the remaining 20 are in METHODEF (example
<https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/util/ResourceBundle.java#L3225>)
and these are just opportunities for small cleanups.
-Archie
--
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20241019/4e51da8e/attachment-0001.htm>
More information about the amber-dev
mailing list