> 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