Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Java > Why is that in JDK8: value used in lambda expression shuld beeffectively final?

Reply
Thread Tools

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

 
 
jeti789@web.de
Guest
Posts: n/a
 
      01-02-2013
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

 
Reply With Quote
 
 
 
 
Steven Simpson
Guest
Posts: n/a
 
      01-02-2013
On 02/01/13 12:51, http://www.velocityreviews.com/forums/(E-Mail Removed) 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

 
Reply With Quote
 
 
 
 
Stefan Ram
Guest
Posts: n/a
 
      01-02-2013
(E-Mail Removed) writes:
>int sum = 0;
>ints.forEach(i -> {sum += i;});
>The compiler error is "value used in lambda expression shuld be effectively final"


I have no knowledge about or experience with the Java 1.8
lambdas, but my idea (made up by me right now) is:

You are passing »i -> {sum += i;}« to »ints«, which (»ints«)
is an object on the heap. This object »ints« may still hold
a reference to »i -> {sum += i;}«, when the method that
contained »int sum = 0;« already has expired. »sum« seems
to be a stack variable. So »ints« then might still hold
»i -> {sum += i;}«, but where is the variable »sum« then?

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


This array lives on the heap and might outlive its function.

The compiler error message sounds strange, because a »value«
cannot be final, only a variable can be final. A value is
something like »4« or »8«. Values never change, even without
»final«.

 
Reply With Quote
 
jeti789@web.de
Guest
Posts: n/a
 
      01-02-2013
Hi Steven,

thanks for your elaborte answer.

>For some definition of closures (one including mutable local capture),
>JDK8 lambdas are not closures (hence, they are not called closures).


I see, I didn't know. I wished Sun/Oracle had made this more clear ...

>AIUI, the main goal of lambdas was to reduce verbosity/overhead of
>anonymous classes used with algorithms that better exploit >parallelism.


Well, JDK8 lambdas how they must be called then do at least reduce the amount of boilerplate code ...

Anyhow, this news does really put a smile on my face. Guess it's really time to look for another JVM-based language like Groovy or Scala .

Regards, Oliver
 
Reply With Quote
 
jeti789@web.de
Guest
Posts: n/a
 
      01-02-2013
> Anyhow, this news does really put a smile on my face.

.... does NOT put a smile on my face, of course. Guess this will give Groovy2.0 more momentum.
 
Reply With Quote
 
Steven Simpson
Guest
Posts: n/a
 
      01-02-2013
On 02/01/13 15:08, (E-Mail Removed) wrote:
> Anyhow, this news does [not] really put a smile on my face. Guess it's really time to look for another JVM-based language like Groovy or Scala .


For your particular case:

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

....the recommended way is to use something like:

int sum = ints.reduce(0, (x, y) -> x + y);

....which is more parallel-friendly. Of course, if you don't care about
the parallelism, it's simply:

int sum = 0;
for (int i : ints)
sum += i;

I'm sure you're just using this example to explore what's available, and
I think it would be a mistake to discard Java based on it. So is there
a more sophisticated case you were hoping would improve with lambdas?


--
ss at comp dot lancs dot ac dot uk

 
Reply With Quote
 
Saxo
Guest
Posts: n/a
 
      01-02-2013
Hm... Maybe it was done that way for backwards compatibility. JDK8 lambdas are like less verbose annonymous classes and nothing more ...
 
Reply With Quote
 
Saxo
Guest
Posts: n/a
 
      01-02-2013
> I'm sure you're just using this example to explore what's available,

Yes, exactly. This little code snippet to calcualate a sum is a very simplebut canonical case of a closure that has a free variable:

"When a function refers to a variable defined outside it, it's called a free variable. A function that refers to a free lexical variable is called a closure.". Paul Graham, ANSI Common Lisp, Prentice Hall, 1996, p.107.

So free variables aren't possible with JDK8 lambdas. For people used to closures, this is surely surprising. Well, this does not render JDK8 lambdas useless. On the contrary, an imense amount of boilerplate code that is necessary without them can be removed with them. Once the JDK8 gets released this will be an eye-opener to about 95% of all Java developers (the ratio of Java developers not understanding closures ...).

> I think it would be a mistake to discard Java based on it.


If I could I would have done that a long time ago. But not going with a M$ language was a deliberate decision and on the JVM nothing will rival Java (only be a complement like Groovy, or scratch on it like Scala or Kotlin).

> int sum = ints.reduce(0, (x, y) -> x + y);


Nice, but it does not compile with me. I use IDEA 12 and the lambda-8-b69-windows-i586-17_dec_2012 JDK. Seems like reduce does not exist in Iterator or Collection!?

-- Oliver



 
Reply With Quote
 
Lew
Guest
Posts: n/a
 
      01-02-2013
Saxo wrote:
> So free variables aren't possible with JDK8 lambdas. For people used to closures, this is surely
> surprising. Well, this does not render JDK8 lambdas useless. On the contrary, an imense amount of
> boilerplate code that is necessary without them can be removed with them. Once the JDK8 gets
> released this will be an eye-opener to
> about 95% of all Java developers (the ratio of Java developers not understanding closures ...).


The ratio of Java developers who do not understand closures is probably pretty close to the
ratio of Java developers who do not understand Java.

Aside from that your comment is unsupportable. Or do you have evidence?

....

I thought not.

--
Lew
 
Reply With Quote
 
Steven Simpson
Guest
Posts: n/a
 
      01-02-2013
On 02/01/13 19:31, Saxo wrote:
>> int sum = ints.reduce(0, (x, y) -> x + y);

> Nice, but it does not compile with me. I use IDEA 12 and the lambda-8-b69-windows-i586-17_dec_2012 JDK. Seems like reduce does not exist in Iterator or Collection!?


I figured, if forEach is on Collection, reduce likely would be too. I
did try to have a look for it in source, but wasn't sure if I had the
right branch. I also didn't find the javadoc on-line. Based on the
source, try:

ints.stream().reduce(0, (x, y) -> x + y)

--
ss at comp dot lancs dot ac dot uk

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
problem in running a basic code in python 3.3.0 that includes HTML file Satabdi Mukherjee Python 1 04-04-2013 07:48 PM
lambda vs non-lambda proc Steve Dogers Ruby 1 03-30-2009 10:11 PM
I have an examination and i shuld answer this question please help me omid1980@gmail.com XML 1 02-18-2007 10:24 PM
findcontrol("PlaceHolderPrice") why why why why why why why why why why why Mr. SweatyFinger ASP .Net 2 12-02-2006 03:46 PM
Re: Lambda as declarative idiom (was RE: what is lambda used for inreal code?) Roman Suzi Python 13 01-07-2005 09:33 PM



Advertisments