I want contribute an observation of a tangential fact about this example. This 
might be interesting reminder in a discussion of the tricky parts of type 
inference.


### The fact

The reason that this example does not compile with Sequenced collections is a 
limitation in the type inference algorithm:

Type constraints that are inferred from instance method calls do not influence 
inference of types in the receiver type expression.

### In the given example

1. `List<Collection<String>> list =` is the target type of `.toList()` so this 
could influence inference of its type parameters.

2. But the inferred type parameters of `.toList()` does not influence the 
inference of types for its receiver expression, `Stream.of(nestedDequeue, 
nestedList)`.

If not for this fact the type of `Stream.of(nestedDequeue, nestedList)` could be inferred 
to be `Stream<Collection>`, but with the current algorithm it will be inferred to be 
`Stream<SequencedCollection>`, leading to the source incompatibility.

Static method calls don't have this limitation. This is a bit weird, because 
instance method mostly act like static method, but with an implicit receiver 
argument. I don't know if there are any strong technical reasons to why there 
is a difference here.

Using a static `toList` method the given example looks like this, and type 
checks also with Sequenced collections:

```
List<Collection<String>> list = toList(Stream.of(nestedDequeue, nestedList));

public static <T> List<T> toList(Stream<T> s) {
    return s.toList();
}
```

(One can change the type of `list` to `List<Iterable<String>>` to experiment 
with this even without Sequenced collections`.)

It would be interesting to have a discussion about whether this limitation 
could be overcome at some other time.

Regards,
Jens Lideström

PS: Type inference algorithms are soooo much fun, aren't they?


On 2023-05-03 15:12, fo...@univ-mlv.fr wrote:
Another example sent to me by a fellow French guy,

     final Deque<String> nestedDequeue = new ArrayDeque<>();
     nestedDequeue.addFirst("C");
     nestedDequeue.addFirst("B");
     nestedDequeue.addFirst("A");

     final List<String> nestedList = new ArrayList<>();
     nestedList.add("D");
     nestedList.add("E");
     nestedList.add("F");

     final List<Collection<String>> list = Stream.of(nestedDequeue, 
nestedList).toList();

This one is cool because no 'var' is involved and using 
collect(Collectors.toList()) instead of toList() solves the inference problem.

Rémi

----- Original Message -----
From: "Stuart Marks" <stuart.ma...@oracle.com>
To: "Remi Forax" <fo...@univ-mlv.fr>
Cc: "core-libs-dev" <core-libs-...@openjdk.java.net>
Sent: Tuesday, May 2, 2023 2:44:28 AM
Subject: Re: The introduction of Sequenced collections is not a source 
compatible change

Hi Rémi,

Thanks for trying out the latest build!

I'll make sure this gets mentioned in the release note for Sequenced
Collections.
We'll also raise this issue when we talk about this feature in the Quality
Outreach
program.

s'marks

On 4/29/23 3:46 AM, Remi Forax wrote:
I've several repositories that now fails to compile with the latest jdk21, which
introduces sequence collections.

The introduction of a common supertype to existing collections is *not* a source
compatible change because of type inference.

Here is a simplified example:

    public static void m(List<Supplier<? extends Map<String, String>>> 
factories) {
    }

    public static void main(String[] args) {
      Supplier<LinkedHashMap<String,String>> supplier1 = LinkedHashMap::new;
      Supplier<SortedMap<String,String>> supplier2 = TreeMap::new;
      var factories = List.of(supplier1, supplier2);
      m(factories);
    }


This example compiles fine with Java 20 but report an error with Java 21:
    SequencedCollectionBug.java:28: error: method m in class 
SequencedCollectionBug
    cannot be applied to given types;
      m(factories);
      ^
    required: List<Supplier<? extends Map<String,String>>>
    found:    List<Supplier<? extends SequencedMap<String,String>>>
    reason: argument mismatch; List<Supplier<? extends 
SequencedMap<String,String>>>
    cannot be converted to List<Supplier<? extends Map<String,String>>>



Apart from the example above, most of the failures I see are in the unit tests
provided to the students, because we are using a lot of 'var' in them so they
work whatever the name of the types chosen by the students.

Discussing with a colleague, we also believe that this bug is not limited to
Java, existing Kotlin codes will also fail to compile due to this bug.

Regards,
Rémi

Reply via email to