Primitive type patterns
Remi Forax
forax at univ-mlv.fr
Thu Apr 7 21:10:52 UTC 2022
> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "amber-spec-experts" <amber-spec-experts at 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20220407/ad37962e/attachment-0001.htm>
More information about the amber-spec-experts
mailing list