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