JDK-8300691 - final variables in for loop headers should accept updates

Archie Cobbs archie.cobbs at gmail.com
Mon Oct 21 15:51:36 UTC 2024


On Mon, Oct 21, 2024 at 10:16 AM Maurizio Cimadamore <
maurizio.cimadamore at oracle.com> wrote:

> I get that! What I'm saying is that most users will read my above example
> like:
>
> for (int j = 0 ; j < 10 ; ) {
>
>    ...
>    j = j + i;
> }
>
> That is a good explanation for what the loop does. But this now breaks the
> generalization (because "j" is now mutated inside the body). E.g. the
> variable "j" is declared in the "for loop" statement. It is also modified
> in the for loop statement (in the STEP part). It seems mostly an
> implementation "trick" that we can look at STEP before BODY, so that we can
> then consider the induction variable as "not mutated after it is
> introduced".
>

Yes but I think this "trick" is appropriate because it aligns with how
developers intuitively view this whole issue.

In other words, the following two examples may be equivalent in terms of
DA/DU analysis, but they are not the same in terms of how ambiguous they
seem to the developer:

Example A:

for (int i = 0; i < 10; i++) {
    Runnable r = () -> System.out.println(i);
}

Example B:

for (int i = 0; i < 10; ) {
    Runnable r = () -> System.out.println(i);
    i++;
}

I am claiming that Example A is much clearer than Example B in terms of
ambiguity (which is the whole problem "effectively final" was invented to
solve).

Now the above claim is a claim about what developers intuitively perceive,
so it's fuzzy by nature and I could certainly be in the minority view here!

All I can confidently say is that after thinking about it and looking at
the examples in the JDK, my own developer intuition would be comfortable
with an "effectively frozen at the point of capture" rule, and I would be
pleased with the resulting ability to get rid of almost all of my "dummy
variables". To me Example A is perfectly clear, while Example B is less
clear.

I'm curious what your "developer intuition" thinks. Do you look at Examples
A and B and perceive them as equally ambiguous?

Taking a step back, the observation that motivates the original proposal
(to me at least) is that developers perceive each iteration of the body of
a for() loop as its own thing, with the loop variable serving the same role
as a method parameter. That is, the body of a for() loop is just a
parameterized block of code - i.e., a thing we usually call a "method" -
that you "invoke" multiple times, and the loop variable is the parameter.
One can view a for() loop as just a special syntax for inlining a method
that you want to invoke multiple times in succession with a sequence of
parameter values. So just as method parameters are effectively final within
their methods, so should loop variables be "effectively final" within their
loop bodies.

If you take that view, then Example A and Example B are clearly different.

They are just as different as these two examples would be:

Example C:

void meth(int i) {
    Runnable r = () -> System.out.println(i);
}

for (int i = 0; i < 10; i++)
    meth(i);

Example D:

void meth(i) {
    Runnable r = () -> System.out.println(i);
    i++;
}

for (int i = 0; i < 10; ) {
    meth(i);
    i++;
}

Again, we're in the domain of "developer intuition" and "perception" so
things are fuzzy... and compiler developers may not constitute a
representative sample :)

-Archie

-- 
Archie L. Cobbs
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20241021/45c241e6/attachment-0001.htm>


More information about the amber-dev mailing list