Delurking comments on the 0.1.5 specification
Reinier Zwitserloot
reinier at zwitserloot.com
Sat Apr 3 03:51:24 PDT 2010
You're mixing up responsibilities. The problem in your example isn't that
Closures cannot access final variables. It's that java isn't expressive
enough to say that the variable in question will not change before the
closure is defined. "Fixing" it by letting closures read/write mutable local
state is not a good fix. You save 5 seconds in your (please stop extending
whole paragraphs to it) "generating customer value" and in turn get a bug
that costs many mandays of work every so often. Bad trade. Proper fixes:
(1) Your idea of moving the posts from 'make variables accessed in closures
effectively final' to 'make variables accessed in closures effectively final
from their point of definition onwards'.
(2) Introduce new syntax to lock in a variable. Something like: "final
first;" (Can be combined with #1, which also makes #1 easier to explain: All
closures insert a virtual "final a, b, c, d, e;" right before you define
them, with all local variables you access.
(3) Add more methods to rewire the logic of this code so that only 1
assignment is ever done (probably closure based. What a coincidence!).
Something along the lines of a comprehension.
--Reinier Zwitserloot
On Fri, Apr 2, 2010 at 3:32 PM, Jim Mayer <jim at pentastich.org> wrote:
> 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