More comprehensive type inference for pattern matching

Nir Lisker nlisker at gmail.com
Thu Oct 10 17:29:32 UTC 2024


Hi Tagir,

class Container<T> {
>   T item1, item2;
> }


Yes, once you add more items it gets to the "bound constraints" I
mentioned. You will need to compute the "lowest common denominator" for the
items. If you check the type of item1 there is mathematically not enough
information to derive the container type - the user can't know that either,
even when solving with pen and paper, so of course you run into problems.
However (and please ignore syntax),

if (item1 instaceof Integer i && item2 instanceof Long l) {
    // I know it's Container<? extends Number>
}

if (item1 instaceof Integer i && item2 instanceof String s) {
    // I know it's Container<? extends Object>
}

It's also possible that it's Container<Integer>, not Container<Number>
>

Isn't it Container<? extends Number>? In any case, at least I know *some*
limitation on the container type.

As for flow-typing, I don't want to get into the syntax level of needing to
bind to a new variable or not, so I'll zoom out a bit.

Given any generic class, we can't access any information about its type
parameter(s) because of erasure, but this is for run-time. For
compile-time, the compiler does many checks for us (otherwise there's no
point for generics at all), but it doesn't use all the information
available. What users tend to find frustrating is that they can derive a
conclusion about the code, but the compiler doesn't do it, so they need to
specify it explicitly in frustration. Nullness analysis is an example ("I
know this can't be null, so why do I need to tell you that?"), and so is
pattern matching for instanceof ("I know this is an X, so why do I need to
tell you that?"). What I can say is: "there is enough type information to
figure out the type parameter, but it does not do so, so I need to do it
explicitly".

Doing instanceof against a type parameter (instanceof T) is impossible
without (partial?) reification, but there are still many valid cases for
wanting to get type info, like switching on Box<String>, Box<Integer> and
Box<Object>. This can be *effectively* available by looking at the content,
I can solve the constraints on a piece of paper. Local type inference (var)
does something similar with the known <Comparable & Serializable> example.
So, I'm not asking for a specific named feature or change like some
"reification", "flow-typing", or "pattern matching", I'm asking to
alleviate the developer from telling the compiler something it can know.

On Thu, Oct 10, 2024 at 1:24 PM Tagir Valeev <amaembo at gmail.com> wrote:

> Hello!
>
> This would basically bring flow-typing to Java (variable type may be
> narrowed at use site, depending on the context; known as smart-casts in
> Kotlin). I think, this is something Java tries to avoid. That's why a
> instanceof B b introduces a new variable, rather than changes the type of a.
>
> Another problem is that container.item instanceof Number does not imply
> container instanceof Container<Number>. It could easily be
> Container<Object>. Imagine
>
> class Container<T> {
>   T item, item2;
> }
>
> If you checked that item is Number, it doesn't mean that item2 is also
> Number. It could be Container<Object> having Integer and String in the
> fields. If you pass it to a place which accepts Container<Number> and reads
> item2, you'll get a problem.
>
> It's also possible that it's Container<Integer>, not Container<Number>.
> Again, if you pass it to the place where Container<Number> is accepted, it
> can rewrite your Integer field with Double, and later the code that still
> assumes it's Container<Integer> will blow up.
>
> With best regards,
> Tagir Valeev
>
>
> On Thu, Oct 10, 2024, 11:17 Nir Lisker <nlisker at gmail.com> wrote:
>
>> Hi,
>>
>> Give a simple generic wrapper,
>>
>> class Container<T> {
>>     T item;
>> }
>>
>> pattern matching can find out what T is for 'item', but not for its
>> container:
>>
>> void switchItem(Container<?> container) {
>>     if (container.item instanceof Number num) {
>>         acceptNumber(num);
>>         acceptNumberContainer(container); // error
>>     }
>> }
>>
>> The information is technically there to make the derivation. I can
>> envision some complexities with bound constraints, but seeing as I can't
>> check 'instanceof Container<Number>`, can the compiler not be smarter?
>>
>> - Nir
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20241010/be8c1b7e/attachment.htm>


More information about the amber-dev mailing list