I've first asked this question about the use of final
with anonymous inner classes in Java:
Why do we use final keyword with anonymous inner classes?
I'm actually reading the Scala book of Martin Odersky. It seems Scala simplifies a lot of Java code, but for Scala closures I could notice a significant difference.
While in Java we "simulate" closures with an anonymous inner class, capturing a final variable (which will be copied to live on the heap instead of the stack) , it seems in Scala we can create a closure which can capture a val, but also a var, and thus update it in the closure call!
So it is like we can use a Java anonymous innerclass without the final
keyword!
I've not finished reading the book, but for now i didn't find enough information on this language design choice.
Can someone tell me why Martin Odersky, who really seems to take care of function's side effects, choose closures to be able to capture both val
and var
, instead of only val
?
What are the benefits and drawbacks of Java and Scala implementations?
Thanks
Related question: With Scala closures, when do captured variables start to live on the JVM heap?
An object can be seen a bag of closures that share access to the same environment and that environment is usually mutable. So why make closures generated from anonymous functions less powerful?
Also, other languages with mutable variables and anonymous functions work the same way. Principle of lease astonishment. Java is actually WEIRD in not allowing mutable variables to be captured by inner classes.
And sometimes they're just darn useful. For example a self modifying thunk to create your own variant of lazy or future processing.
Downsides? They have all the downsides of shared mutable state.
Here are some benefits and drawbacks.
There is a principle in language design that the cost of something should be apparent to the programmer. (I first saw this in Holt's Design and Definition of the Turing Language, but I forget the name he gave it.) Two things that look the same should cost the same. Two local vars should have similar costs. This is in Java's favour. Two local vars in Java are implemented the same and so cost the same regardless of whether one of them is mentioned in an inner class. Another point in Java's favour is that in most cases the captured variable is indeed final, so there is little cost to the programmer to be prevented from capturing nonfinal local vars. Also the insistence on final simplifies the compiler, since it means that all local variables are stack variables.
There is another principle of language design that says be orthogonal. If a val can be captured why not a var. As long as there is a sensible interpretation, why put in restrictions. When languages are not orthogonal enough, they seems perverse. On the other-hand, languages that have too much orthogonality may have complex (and hence buggy and/or late and/or few) implementations. For example Algol 68 had orthogonality in spades, but implementing it was not easy, meaning few implementations and little uptake. Pascal (designed at about the same time) had all sorts of inelegant restrictions that made writing compilers easier. The result was lots of implementations and lots of uptake.