Accessing non-final local variables from a lambda expression
Neal Gafter
neal at gafter.com
Thu Feb 25 23:36:12 PST 2010
I experimented a bit with a lambda-ized version of a modified Google
Collections. Specifically, I was playing with the method
<T> Iterable<T> filter(
Iterable<T> input,
Predicate<T> predicate)
{ ... }
Using this method and an anonymous inner class, one can write a method that
returns every other value in its input sequence:
// before
<T> Iterable<T> everyOther(Iterable<T> input) {
return filter(input, new Predicate<T>{
boolean flag = true;
public boolean apply(T input) {
return (flag ^= true);
}
});
}
The equivalent function-ized version of the API would be (expressed in my
preferred syntax):
<T> Iterable<T> filter(
Iterable<? extends T> input,
(T)->boolean predicate)
{ ... }
But now I have a problem implementing everyOther. The only way to create a
value of function type is to write a lambda expression, but where to put the
flag? In BGGA, CfJ, and other languages with lambdas, one could write it
simply like this
<T> Iterable<T> everyOther(Iterable<T> input) {
boolean flag = true;
return filter(input, (T t)->(flag ^= true));
}
But project lambda forbids access to mutable local variables from the
enclosing scope. I came up with the following workable approach
// after
<T> Iterable<T> everyOther(Iterable<T> input) {
class Alternator {
boolean flag = true;
boolean invoke() {
return (flag ^= true);
}
}
final Alternator alternate = new Alternator();
return filter(input, #(T t)(alternate.invoke());
}
Comparing the before and the after, it appears that project lambda didn't do
us any favors here. The "after" code is far worse. In our attempt to make
the concurrent use cases "easier not to get wrong", we've placed a pile of
obstacles before every programmer who uses function types in sequential
code.
It would be better to give a warning (not error) when a lambda accesses a
mutable local variable from an enclosing scope, and provide one or more easy
ways for programmers to suppress the warning. See CfJ <
http://www.javac.info/closures-v06a.html> for my preferred approach.
More information about the lambda-dev
mailing list