4 ms·
> It is our intent to prohibit capture of mutable local variables. The reason is that idioms like this: int sum = 0; list.forEach(e -> { sum += e.size();
by ScottBurson 14y ago
> It is our intent to prohibit capture of mutable local variables. The reason is that idioms like this:
int sum = 0;
list.forEach(e -> { sum += e.size(); });
> are fundamentally serial
An unfortunate decision. Yes, updating such a variable from multiple threads can cause race conditions. But there are many ways to run into race conditions when writing multithreaded code, and this doesn't really add a new one.
Furthermore, most code is mostly serial, and the ability to modify captured variables is a very useful one in serial code. (I've written code like that in Lisp for decades.) And finally, the restriction is, of course, easily worked around -- just store the value you want to update in a 1-element array.
So the restriction doesn't really protect anyone from anything -- it just forces them to use an ugly workaround in order to write their algorithms in a natural way.
- seanmcdirmid 14y agoIronically, "sum += e.size()" is conceptually not serial at all; the order that each iteration runs is not relevant while you could restructure the computation into a tree of partial aggregations. The work around in Java, using a Cell<T>, is a hack that gives you the updatable mutable variable you need without too much ugly overhead. so something like... cell = new Cell<T>(); ... { ... cell.Value = ... } })
- kevinpet 14y agoThe motivation is that the question of whether list.forEach (or maybe better, iterable.forEach) is serial or parallel is to be left up to the implementation of the collection. Everything related to multi-threaded code since about Java 5 has been about hiding the details of concurrency and allowing programmers to think about code in a serial fashion, but still get the gains from multi threaded code. It's not necessarily about parallelism either -- you could just as easily pass in a function as some sort of callback that gets called far in the future, for example, String error = null; return createClientWithErrorHandler(e -> { error = e; }); What does that even mean? Does the execution context need to be kept hanging around? Java already provides good atomic references if you need to have a sum variable or anything similar.
- ScottBurson 14y ago> What does that even mean? Does the execution context need to be kept hanging around? Once the block containing the declaration of 'error' has been exited, the only way anything could refer to 'error' would be if there were at least one additional lambda expression in the same block that was closed over the same binding of 'error'. Two such lambda expressions could thus communicate with each other through the shared binding.