Delurking comments on the 0.1.5 specification

Jim Mayer jim at pentastich.org
Fri Apr 2 06:32:31 PDT 2010


On Fri, Apr 2, 2010 at 4:14 AM, Reinier Zwitserloot
<reinier at zwitserloot.com> wrote:

> Can you give an example of this? If a closure references a variable, and
> other 'seemingly disconnected' code no longer compiles due to the need for
> that variable to be effectively final, clearly it wasn't *actually*
> disconnected and the compiler just did you a great favour and saved you lots
> of time in debugging.
>

Sure,

Do you think that  'spatially disconnected' would have been a better
choice of words than 'apparently disconnected'?

Anyway, consider the following example:

class C {
    void m(Collection<Integer> data) {
        Integer first = null;
        for (Integer d : data) {
            switch (d) {
            case 11:
                // something
                break;
            case 42:
                if(first != null)
                    first = d;
                break;
            case 99:
                // something
                break;
            }
        }
        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                System.out.println(first);
            }
        });
    }
}

I've used anonymous inner classes so the code will actually compile if
you fix the imports.  The issues are the same.

As written, the compiler complains about a reference to a non-final
'first' within an anonymous inner class.  If I add a 'final' to the
declaration of 'first', the compiler complains about the assignment to
'first' under case '42' of the switch.  Because the developer has
indicated their intent with the 'final' declaration it is very clear
who's breaking the language rules.

With 'effectively final', however, the compiler really should report
two errors: one at each assignment and another at each use within a
lambda expression.  The reason is that, from a developer perspective,
it's not clear whether the "fault" lies with "use of a non-final
variable within a closure" or the "assignment to a variable used
within a closure".

Another thing to note is that, in this instance, the compiler hasn't
actually protected the developer from anything since this code is
perfectly safe.  It can be trivially "fixed" by changing the last
lines to:

        final Integer finalFirst = first;

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                System.out.println(finalFirst);
            }
        });

A couple of thoughts:

(1) To me, the real problem is that "fixing" trivial stuff like this
distracts developers from their actual job.  Programming isn't about
being able to make a particular programming language jump through
hoops, it's about the stunningly difficult problem of turning squishy
concepts into concrete code.  In LEAN terms, anything that distracts
developers from generating customer value is waste.  The benefits of
avoiding some pitfalls may outweigh the cost of the waste, but we
should still strive to minimize it.

(2) A good IDE would make most of this pain go away as the IDE could,
on the fly, flag both the questionable assignments AND the
questionable uses.

(3) Introducing "@Shared" makes a lot, but not all, of the problem go away.

(4) I bet that if we loosened the definition of "effectively final" to
be "is not assigned to in any code that is reachable from the point
where the lambda expression is defined" then nearly all of the trivial
annoying cases would go away.

Hmm... I kind of like (4).  Any thoughts?

-- Jim


More information about the lambda-dev mailing list