eclipse-archived / ceylon

The Ceylon compiler, language module, and command line tools
http://ceylon-lang.org
Apache License 2.0
399 stars 62 forks source link

Nullability for return values of Java methods with generic type. #7287

Open dlkw opened 6 years ago

dlkw commented 6 years ago

Given a method of a Java class (and the used interface)

public <T> T performInTransaction(TransactionalOperation<T> op);

public interface TransactionalOperation<T> {
    T perform();
}

The method is visible in Ceylon as

shared T performInTransaction<T>(TransactionalOperation<T> op)
    given T satisfies Object;

which inhibits return value null.

A Java library might not provide an additional implementation for return type void, because that can be "emulated" using Java's Void type with its null value. Using it this way with Ceylon callables returning null (or void functions) is not possible, but should be.

jvasileff commented 6 years ago

I believe the general problem is that given the way interop model loading works, you cannot use an optional type as a type argument to a Java method. You can however use optionals as type arguments to types, which seems fine.

A couple notes:

@dlkw confirmed on gitter that the same problem exists when attempting to pass a TransactionalOperation<String?> to performInTransaction().

Renato also ran into this restriction attempting to call ExecutorService.submit(). See https://gitter.im/ceylon/user?at=567076228b28de8704521446

dlkw commented 6 years ago

I just noticed the very strange error message: Argument must be assignable to parameter op of performInTransaction in ServerRuntime: Result() is not assignable to <Result&Object>?() in the following code:

shared Result execute<Result>(callable)
{
    Result() callable;
    value result = performInTransaction(()
    {
        Result result = callable();
        return result;
    });
}
dlkw commented 6 years ago

The following does compile, I'm wondering what's happening here:

shared Result execute<Result>(callable)
{
    Result() callable;
    value result = performInTransaction(()
    {
        Result result = callable();
        if (is Null result) {
            return result;
        }
        return result;
    });
}
dlkw commented 6 years ago

Rejoiced too early: it gives a backend error: Ceylon backend error: incompatible types: java.lang.Object cannot be converted to Result