transparent lambda
Peter Levart
peter.levart at marand.si
Mon Jan 4 02:07:45 PST 2010
Hello all and happy New Year!
Here's another example:
#int() one = null;
synchronized int sum(int n) {
if (one == null) one = #() { return 1; };
return n == 0 ? 0 : sum(n-1) + one();
}
What does initial call to sum(2) return?
1. returns 2
2. throws an exception
3. returns 1
The first call to lambda 'one' happens in a nested call to 'sum'. Should this be considered "different" invocation of method 'sum' from the one that lambda expression was evaluated in? Is the answer: "Yes, but unmatched local transfer exception is not thrown because the invocation of 'sum' that evaluated lambda expression is still active, so the transparent "return 1" returns from the first, not the nested, invocation of 'sum'"?
@Neal: You breefly mentioned the following possible technique for identifying the "constructing" invocation:
"When a closure captures some context from the enclosing scope, that context can be packaged up
into a frame object. The identity of that frame object can be used to
distinguish one invocation from another."
What about the following idea:
public class NonLocalReturnInt extends RuntimeException {
public final Object lambda;
public final int retval;
public NonLocalReturn(Object lambda, int retval) {
this.lambda = lambda;
this.retval = retval;
}
}
The compiler could generate the following code for the above example:
#int() one = null;
synchronized int sum(int n) {
#int() $tmpLambda = null;
if (one == null) {
$tmpLambda = new #int()() {
public int invoke() { throw new NonLocalReturnInt(this, 1); }
};
one = $tmpLambda;
}
try {
return n == 0 ? 0 : sum(n-1) + one();
}
catch (NonLocalReturnInt $nlr) {
if ($tmlLambda == $nlr.lambda)
return $nlr.retval;
else
throw $nlr;
}
}
Each lambda expression reserves a slot on the stack which is filled-in with a reference to the lambda when the lambda expression is evaluated. The "NonLocalReturn" exception also contains a reference to the lambda that initiated the non-local transfer. Each invocation of a lambda in a method is wrapped in a try/catch that catches the "NonLocalReturn" and checks it's "lambda" reference with all the "$tmpLambda" slots. If any of them matches, non-local return is translated into a return from the method else it is re-thrown.
This is just a rough idea. It would need some refinements, in particular it should be generalized to other kinds of non-local transfers, including labeled lambda yields. But it's advantage I think is, that it does not allocate any additional objects than those already needed.
For the above example, the lambda does not capture anything from enclosing scope, so it could still be optimized away into a static field somewhere.
Regards, Peter
On Wed Dec 30 08:42:23 PST 2009 Neal Gafter wrote:
> I'm trying to avoid a "solution" that allows this puzzler to be puzzling:
#int() two = null;
synchronized int four() {
if (two == null) two = #() { return 2; };
return two() + two();
}
public static void main(String[] args) {
System.out.println(four());
}
What does this do?
1. prints "4"
2. throws an exception
3. prints "2"
4. Sometimes prints "2", sometimes throws an exception
5. does not compile
If you make statement lambdas transparent, the answer is choice 4: the
method prints "2" on its first execution, and throws an exception (unmatched
transfer) thereafter.
More information about the closures-dev
mailing list