> From: "Brian Goetz" <brian.go...@oracle.com>
> To: "amber-spec-experts" <amber-spec-experts@openjdk.java.net>
> Sent: Thursday, April 7, 2022 9:54:11 PM
> Subject: Re: Primitive type patterns

> There's another, probably stronger, reason why primitive patterns supporting
> widening, narrowing, boxing, unboxing, etc, are basically a forced move,
> besides alignment with `let` statements, discussed earlier:

>> There is another constraint on primitive type patterns: the let/bind 
>> statement
>> coming down the road. Because a type pattern looks (not accidentally) like a
>> local variable declaration, a let/bind we will want to align the semantics of
>> "local variable declaration with initializer" and "let/bind with total type
>> pattern". Concretely:

>> let String s = "foo";

>> is a pattern match against the (total) pattern `String s`, which introduces 
>> `s`
>> into the remainder of the block. Since let/bind is a generalization of local
>> variable declaration with initialization, let/bind should align with locals
>> where the two can express the same thing. This means that the set of
>> conversions allowed in assignment context (JLS 5.2) should also be supported 
>> by
>> type patterns.

> The other reason is the duality with constructors (and eventually, methods). 
> if
> we have a record:

> record R(int x, Long l) { }

> we can construct an R with

> new R(int, Long) // no adaptation
> new R(Integer, Long) // unbox x
> new R(short, Long) // widen x
> new R(int, long) // box y

> Deconstruction patterns are the dual of constructors; we should be able to
> deconstruct an R with:

> case R(int x, Long l) // no adaptation
> case R(Integer x, Long l) // box x
> case R(short s, Long l) // range check
> case R(int x, long l) // unbox y, null check

> So the set of adaptations in method invocation context should align with those
> in nested patterns, too.
We already discussed those rules when we discuss instanceof, it means that "x 
instanceof primitive" has different meaning depending on the type of x 
Object x = ... 
if (x instanceof short) { ... } // <=> instanceof Short + unboxing 

int x = ... 
if (x instanceof short) { ... } // <=> range check 

Byte x = ... 
if (x instanceof short) { ... } // <=> nullcheck + unboxing + widening 

You are overloading instanceof with different meanings, losing everybody apart 
the compiler in the process. 

It's also another creative way to have an action at distance, 
var x = ... 
f (x instanceof short) { ... } // <=> ??? 

We do not need all theses conversions to be part of the pattern, those 
conversions are already done as part expression/instruction after the ':' or 
'->' of the switch in an organic way. 
// with op(int, Long) 
case R(int x, Long l) -> op(x, l); // no adaptation 

// with op(Integer, Long) 
case R(int x, Long l) -> op(x, l); // box x 

// with op(int, long) 
case R(int x, Long l) -> op(x, l); // unbox l, nullcheck 

And for the range check, as i said earlier, it's better to use a pattern method 
with a good name so everybody will be able to read the code. 

RĂ©mi 

Reply via email to