JDK-7120669
John Napier
jnape09 at gmail.com
Sat Aug 8 23:21:25 UTC 2020
Hi Dan, thanks very much for the reply!
We regularly incorporate an approach to mixing in behaviors via interfaces that retain type-level evidence of the specific covariant subtype in question, generally through the liberal use of CRTP with a witness over some parametrized bivariant constraint. As an example, consider the following interface:
interface List<A, Witness extends List<?, Witness>> {
<B> List<B, Witness> map(Function<? super A, ? extends B> fn);
}
Now, say I have two interface subtypes, LinkedList and ConcurrentList, and I implement them using a common type, ConcurrentLinkedList:
interface LinkedList<A, Witness extends LinkedList<?, Witness>> extends List<A, Witness> {
@Override
<B> LinkedList<B, Witness> map(Function<? super A, ? extends B> fn);
LinkedList<A, Witness> cons(A a);
}
interface ConcurrentList<A, Witness extends ConcurrentList<?, Witness>> extends List<A, Witness> {
@Override
<B> ConcurrentList<B, Witness> map(Function<? super A, ? extends B> fn);
<B> ConcurrentList<B, Witness> parMap(Function<? super A, ? extends B> fn, Executor ex);
}
public static final class ConcurrentLinkedList<A> implements
LinkedList<A, ConcurrentLinkedList<?>>,
ConcurrentList<A, ConcurrentLinkedList<?>> {
@Override
// diamond addressed via common covariant bound
public <B> ConcurrentLinkedList<B> map(Function<? super A, ? extends B> fn) {
throw new UnsupportedOperationException("elided");
}
@Override
public ConcurrentLinkedList<A> cons(A a) {
throw new UnsupportedOperationException("elided");
}
@Override
public <B> ConcurrentLinkedList<B> parMap(Function<? super A, ? extends B> fn, Executor ex) {
throw new UnsupportedOperationException("elided");
}
}
So far, so good.
However, suppose I want to (and I certainly do) demand some implementation of both LinkedList and ConcurrentList without prescribing a specific subtype implementation in order to remain open to alternative concrete implementations that satisfy both type signatures: if you’d like to use ConcurrentLinkedList, you may; if you have any other implementation, that’s also fine. It might look like this:
public static <A, W extends LinkedList<A, W> & ConcurrentList<A, W>> void withConcurrentLinkedList(W w) {
throw new UnsupportedOperationException("elided");
}
Unfortunately, javac considers the above intersection constraint on W to be ill-typed; theoretically uninhabitable, despite the algebra for subtype polymorphism obviously supporting it, as evidenced by the fact that javac considers the ConcurrentLinkedList implementation to be well-typed. Given that there are infinitely many proper subtypes of both LinkedList and ConcurrentList that can resolve the diamond conundrum by simply advertising themselves in the return type of any conflicting methods (map, for example), to miss out on the ability to allow any single one of them to be passed into this method due to this oddly schizophrenic limitation really undermines this potential (and in my team’s view, very desirable) solution to the expression problem.
In the service of intellectual honesty and to cover all the bases, one proposed solution might be to export all useful combinations of interfaces following this pattern (promoting ConcurrentLinkedList to an interface, for example), but this very quickly results in a combinatorial explosion of interfaces that detonates again every time an additional interface is added later (in fact, the better your interfaces are segregated, the worse the experience is!).
Does that make sense and provide enough context?
Thanks again for the follow up!
John
> On Aug 4, 2020, at 6:37 PM, Dan Smith <daniel.smith at oracle.com> wrote:
>
>> On Jul 22, 2020, at 2:45 PM, John Napier <jnape09 at gmail.com> wrote:
>>
>> Hey all,
>>
>> https://bugs.openjdk.java.net/browse/JDK-7120669 is a bug I and my teams at work run into all the time: I was curious if there was any plan to incorporate a fix for it in an upcoming release, and if so, what the intended resolution was?
>
> Hi,
>
> Appreciate the interest. If you can provide some examples of where this bug causes you problems (program won't compile? inconsistency between javac and Eclipse? unexpected runtime behavior?), that would be really useful.
>
> A concrete plan doesn't exist yet, but it will involve finding a consensus about which intersection types should be considered valid and which should not. May involve some new compiler errors and/or successful compilation of previously-rejected code.
>
> There is some interest in addressing issues like this as we push on the type system with inline classes in Valhalla and sealed classes in Amber.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20200808/f49bc8d3/attachment.htm>
More information about the compiler-dev
mailing list