On 22.08.2017 14:11, Jesper Steen Møller wrote:
It's two problems in one.

A) The code generation itself is not quite so bad as you might think, especially if we for a moment ignore serializable lambdas. For instance, here's how Eclipse's JDT does it:
http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/tree/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java#n178

It's a matter of recording a bootstrap method in the constant pool of the class, and then emitting a invokeDynamic to "hit" that bootstrap method.

B) The really tricky issue is the type analysis you need to have in place to figure out the type signature of the Lambdas type signature and implementation method. Both javac and ECJ grew significantly in complexity in working to find this out, including backtracking. The choice of the lambda's type will in turn affect which possible overload to call.

Only the static compilation will have a chance to do this right, and getting it "perfect" (including variable and member capture) is pretty tricky.

This can shed some light on the comments above:

    A lambda expression (§15.27) is potentially compatible with a functional 
interface type (§9.8) if all of the following are true:
      *  The arity of the target type's function type is the same as the arity 
of the lambda expression.
      *  If the target type's function type has a void return, then the lambda 
body is either a statement expression (§14.8) or a void-compatible block 
(§15.27.2).
      *  If the target type's function type has a (non-void) return type, then 
the lambda body is either an expression or a value-compatible block (§15.27.2).


We would have no problem with the arity if we stay with the lambda syntax. A Closure expression would already have problems here. But without looking at the code in more detail you cannot say if the lambda is actually returning something. and if you have ->System.out.println(1); You need to know if println is void or not to be sure if the lambda returns something or not. That alone already implies a lot of compiler work we do only in the static compiler right now. But that is not enough:

void f(Function<String, Integer> x) {}
void f(IntFunction<String> x) {}
f(x -> x.hashCode());

which one is to be taken here? For the IntFunction version we actually would have a resulting signature for the lambda of (String)int, for the Function (String)Integer. I think the java compiler will fail to compile this. But what could a dynamic Groovy do here?

As for static compilation... I think what we could implement which is not too complicated is to actually first determine the arity to get potentially fitting signatures and ignoring the return type, then apply the signature to the types of the lambda and see if that will compile and what the return type will be. If that would compile and the return type fits, we have a match. Then we would proceed with the next signature. It is ambiguous if there is more than one match.

I think with this simple approach we should cover all problems I know in combination with lambdas. And of course the case above would then not compile because int and Integer are both valid return types. Would be nice if somebody could show me a counter example

bye Jochen

Reply via email to