Project Lambda: Java Language Specification draft 0.1.5

Neal Gafter neal at gafter.com
Fri Feb 12 19:04:46 PST 2010


A few flubs and omissions that I noticed:

- Your grammar is ambiguous.  Expression goes to both LambdaExpression
and (indirectly) to Primary, both of which have a way of writing a
lambda.

- It is unclear what the type of "this" is in an expression lambda. Is
it forbidden?

- I wonder if the identity of a lambda can change spuriously.  For
example, if "this" refers to the lambda expression, can it be expected
to have the same value on each invocation of the same lambda
expression?  In other words,

#Object() self = #()this;
assert self.() == self.(); // guaranteed??

- Similar question if the lambda has been converted to a SAM

Object() self1 = #()this;
Callable<Object> self2 = self1;
assert self1.() == self2.call(); // guaranteed??

- What is the meaning of "toString()" inside a lambda?  Does it refer
to the method inherited by the lambda from Object, or from the
enclosing scope?

- It isn't clear why you would want special rules for recursive
lambdas in this revision of the spec, given that one could just invoke
"this" inside the lambda.  Or, failing that, just use a method or an
anonymous inner class.

- In your example where there is an "unreachable return statement",
the return statement is (formally) reachable.  Any unreachable return
statement would be a compile-time error because it is an error to have
an unreachable statement in Java.

- Your definition of effectively final mentions "target of an
initialization", but that doesn't make sense.  It can only be the
target of an initialization in its declaration, and it is always
definitely unassigned before then.  (Yes, even local variables in
switch statements)

- The spec "It is a compile-time error to modify the value of an
effectively-final variable in the body of a lambda expression." is
meaningless.  If there were an assignment to a variable from an
enclosing scope in a lambda, the variable would not be definitely
unassigned at that point, and so by definition the variable would not
be effectively-final.  So this rule could never be applied in the way
you appear to intend.  The only way the rule could be applied is
definitely something you do not want to forbid:

Object o = #(){
    int x; // x is effectively final
    x = 3; /* error: It is a compile-time error to modify the value of
an effectively-final variable in the body of a lambda expression */
};

- Your example "The constraint #int(String) >> #int(T) resolves to
String>>T" in incorrect.  It resolves to T>>String.

- Your inference algorithm doesn't allow the use of the lambda
conversion.  Specifically, if you try to pass
  #()("Foo")
to a method
  <T>void f(Callable<T>);
there is no way to infer that T=String.

- Your assignment conversion doesn't allow a subtype conversion
followed by a lambda conversion.  Consequently, you may force
programmers to use temporary variables just to force the compiler to
do a subtype conversion before the lambda conversion.  In expression
contexts (i.e. super() invocations) that is cumbersome indeed.

- Your rules for lambda conversion appear to improperly erase some
types.  For example, the following code is ruled illegal

interface Receiver<T> { void f(T); }
Receiver<List<String>> rls = #(List<String> ls)(); // error:
"List<String>" is not the same as "List".

- The specification for a SAM type says that "If a SAM type is a class
type, then it must be declared abstract and have an accessible no-args
constructor.".  That makes the following (currently legal code)
illegal:

abstract class SAM { private SAM(int arg){} abstract void f(); } //
error: SAM type does not have no-args constructor

- There are no accessibility constraints on the methods of a SAM.
What if the abstract method is package-private to someone else's
package?  Or, what if there are two override-equivalent abstract
methods that are package-private to different packages?  Generating
correct code for (a lambda conversion in) this case is extremely
cumbersome, and may require generating three or more classes in
different packages.

- The following is a SAM (it has only one non-generic abstract
method), but should not be considered a SAM

interface SAM { void foo(); <T> void bar(int x); }

- Your specification [11.2.2] makes no sense.  You have the exceptions
from the body of a statement lambda being thrown where the lambda
expression appears.  A lambda expression should throw no checked
exceptions.

- You don't describe the cases in which a lambda conversion can throw
a checked exception. For example, what if the SAM constructor declared
an exception?

- Your handling of exceptions in the lambda conversion is not correct.
 For example

interface I1 { void f() throws X1; }
interface I2 { void f() throws X2; }
interface I3 : I1, I2 {}

I3 x = #(){ throw new X1(); } // allowed, even though I2.f() doesn't throw X1.

- You say "let M be the set of methods..." but you mean "let S be the
set of abstract methods...".

Cheers,
Neal


More information about the lambda-dev mailing list