Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.java.programmer > #20869 > unrolled thread

Why is that in JDK8: value used in lambda expression shuld be effectively final?

Started byjeti789@web.de
First post2013-01-02 04:51 -0800
Last post2013-01-05 07:01 -0800
Articles 5 — 4 participants

Back to article view | Back to comp.lang.java.programmer


Contents

  Why is that in JDK8: value used in lambda expression shuld be effectively final? jeti789@web.de - 2013-01-02 04:51 -0800
    Re: Why is that in JDK8: value used in lambda expression shuld be effectively final? Steven Simpson <ss@domain.invalid> - 2013-01-02 14:15 +0000
      Re: Why is that in JDK8: value used in lambda expression shuld be   effectively final? Robert Klemme <shortcutter@googlemail.com> - 2013-01-07 23:08 +0100
    Re: Why is that in JDK8: value used in lambda expression shuld be effectively final? Saxo <jeti789@web.de> - 2013-01-02 09:46 -0800
    Re: Why is that in JDK8: value used in lambda expression shuld be effectively final? Saxo <jeti789@web.de> - 2013-01-05 07:01 -0800

#20869 — Why is that in JDK8: value used in lambda expression shuld be effectively final?

Fromjeti789@web.de
Date2013-01-02 04:51 -0800
SubjectWhy is that in JDK8: value used in lambda expression shuld be effectively final?
Message-ID<c885bedb-977c-49b4-a10a-b9cdd3df1dd9@googlegroups.com>
I'm currently plaing with JDK8 and using lambda-8-b69-windows-i586-17_dec_2012. I wonder why this does not compile:

List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
ints.add(3);

int sum = 0;
ints.forEach(i -> {sum += i;});

The compiler error is "value used in lambda expression shuld be effectively final"

However, this here compiles:

int sumArray[] = new int[] { 0 };
ints.forEach(i -> {sumArray[0] += i;});

I'm a bit confused now. Does this mean that JDK8 closures are no true closures or is this a bug in my IDE (IDEA 12)?

Thanks, Oliver

[toc] | [next] | [standalone]


#20870

FromSteven Simpson <ss@domain.invalid>
Date2013-01-02 14:15 +0000
Message-ID<m0sdr9-0tg.ln1@s.simpson148.btinternet.com>
In reply to#20869
On 02/01/13 12:51, jeti789@web.de wrote:
> I'm currently plaing with JDK8 and using lambda-8-b69-windows-i586-17_dec_2012. I wonder why this does not compile:
>
> List<Integer> ints = new ArrayList<>();
> ints.add(1);
> ints.add(2);
> ints.add(3);
>
> int sum = 0;
> ints.forEach(i -> {sum += i;});
>
> The compiler error is "value used in lambda expression shuld be effectively final"

It's the same reason why inner classes can't mutate local variables in 
the enclosing scope.  The method that accepts the lambda or inner class 
instance cannot formally make any guarantee not to invoke the supplied 
code in a non-serial way.

forEach could informally specify that it will execute its argument only 
on its calling thread, and not after it returns, but the compiler could 
not detect that guarantee and allow 'sum' to be modified.

(I'm using 'formal' to indicate something that can be detected by the 
compiler.)


> However, this here compiles:
>
> int sumArray[] = new int[] { 0 };
> ints.forEach(i -> {sumArray[0] += i;});

Ultimately, the compiler can't stop anyone from shooting themselves in 
the foot.  You can still access an instance variable unsafely via a 
lambda, for example.

Stopping the protection at the level of local variables could seem a bit 
arbitrary, but it is still useful, since you expect the use of local 
variables to be intrinsically thread-safe (and they remain so under this 
level of protection), and trying to provide more comprehensive 
protection would be increasingly difficult.


> I'm a bit confused now. Does this mean that JDK8 closures are no true closures or is this a bug in my IDE (IDEA 12)?

For some definition of closures (one including mutable local capture), 
JDK8 lambdas are not closures (hence, they are not called closures).  
AIUI, the main goal of lambdas was to reduce verbosity/overhead of 
anonymous classes used with algorithms that better exploit parallelism.  
These do not typically require or work well with shared variables, and 
supporting mutable locals efficiently would require considerable extra 
effort for something substantially beyond the main goal.

I'm sure you can get a more authoritative explanation by checking the 
lambda mailing-list archives:

<http://mail.openjdk.java.net/pipermail/lambda-dev/>

I suggest looking for terms such as 'mutable local capture', and 
'effectively final'.


-- 
ss at comp dot lancs dot ac dot uk

[toc] | [prev] | [next] | [standalone]


#21169 — Re: Why is that in JDK8: value used in lambda expression shuld be effectively final?

FromRobert Klemme <shortcutter@googlemail.com>
Date2013-01-07 23:08 +0100
SubjectRe: Why is that in JDK8: value used in lambda expression shuld be effectively final?
Message-ID<al0vaqF5lhU1@mid.individual.net>
In reply to#20870
On 02.01.2013 15:15, Steven Simpson wrote:
> On 02/01/13 12:51, jeti789@web.de wrote:
>> I'm currently plaing with JDK8 and using
>> lambda-8-b69-windows-i586-17_dec_2012. I wonder why this does not
>> compile:
>>
>> List<Integer> ints = new ArrayList<>();
>> ints.add(1);
>> ints.add(2);
>> ints.add(3);
>>
>> int sum = 0;
>> ints.forEach(i -> {sum += i;});
>>
>> The compiler error is "value used in lambda expression shuld be
>> effectively final"
>
> It's the same reason why inner classes can't mutate local variables in
> the enclosing scope.  The method that accepts the lambda or inner class
> instance cannot formally make any guarantee not to invoke the supplied
> code in a non-serial way.

I don't think concurrency is the main reason.  It has more to do with 
the complexity involved in keeping the surrounding scope (local 
variables on the stack) alive for an _indefinite amount of time after 
the invocation of a method_.  For mutations of references or POD values 
in the surrounding scope this scope would need to stay around for as 
long as the inner / anonymous class instance lives.  In this case

public Runnable sample(int count) {
   return new Runnable() {
     public void run() {
       while ( count > 0 ) {
         --count;
       }
     }
   };
}

variable "count" would need to be assignable at least as long as the 
created instance lives.  Same for any local variable declared inside the 
method body.  If it weren't we would not have proper closure semantics 
(e.g. when returning two instances which refer the same variable from 
the scope).  Since the scope need to be kept for longer, it cannot 
reside on the stack.  That would introduce two different ways of local 
variable access: one via the stack and one via some reference on the 
heap.  I guess since variable access is such a fundamental feature 
language designers did not want to introduce this type of complexity. 
(There could also be performance issues lurking.)

With "final" variables it's easy: just copy all used values or 
references in hidden member variables of the created instance and be 
done.  Because original variables are final the scope does not need to 
be kept around: since state of variables in the scope cannot change both 
values cannot digress.  And actually this is what is happening, as you 
can see when disassembling code.

Example at github because the disassembly output is quite long.
https://gist.github.com/4478864

The member is declared in line 140 and the constructor in 146.  Also you 
can see that the value of this field is used in line 195.

This does not change in Java 8 because compatibility with old code was 
intended.  lambdas are merely syntactic sugar for "old" Java features. 
For anyone interested in the details I recommend to read

http://cr.openjdk.java.net/~briangoetz/lambda/collections-overview.html

Kind regards

	robert

-- 
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestpractices.com/

[toc] | [prev] | [next] | [standalone]


#20872

FromSaxo <jeti789@web.de>
Date2013-01-02 09:46 -0800
Message-ID<ac485316-8e79-4e92-b81a-21b6189cb6c6@googlegroups.com>
In reply to#20869
Hm... Maybe it was done that way for backwards compatibility. JDK8 lambdas are like less verbose annonymous classes and nothing more ...

[toc] | [prev] | [next] | [standalone]


#20987

FromSaxo <jeti789@web.de>
Date2013-01-05 07:01 -0800
Message-ID<1cb8648d-4fe3-40f9-b433-ee027770746a@googlegroups.com>
In reply to#20869
Having to declare the free variable of lambda expression final has also it's advantages. This here is taken from the JDK8 lambda FAQ (http://www.lambdafaq.org/what-are-the-reasons-for-the-restriction-to-effective-immutability):

"The restriction on local variables helps to direct developers using lambdas aways from idioms involving mutation; it does not prevent them. Mutable fields are always a potential source of concurrency problems if sharing is not properly managed; disallowing field capture by lambda expressions would reduce their usefulness without doing anything to solve this general problem."

Question is whether this is intentional or a pretext. Unhappily, it also does not matter since this restriction can be circumvented anyway with the single-element-array trick:

final int sumArray[] = new int[] { 0 };
ints.forEach(i -> {sumArray[0] += i;}); 
println(sumArray[0]); 

Whether you declare sumArray final here or not makes no difference in the matter. 

Your idea with @Share is interesting. There is f.ex. @Immutable in Groovy. In Groovy you can define those things like your @Share annotation through AST transformations and this way make additions to the language. You can also define such AST transformations in Scala where they are called macros.

If you are interested in those things Groovy or Scala may be worth a look :-).

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.java.programmer


csiph-web