Patterns design question: Primitive type tests
brian.goetz at oracle.com
Sat Nov 4 22:02:05 UTC 2017
Here’s a little more detail on this choice. Assuming that we:
- Outlaw primitive type-test patterns, except in type-restating contexts;
- Outlaw numeric constant patterns, except where the switch target type is a primitive or a box type.
We can now define how we can validate whether patterns are potentially applicable (and statically reject those that are not.)
Let's say that the static type of the target is S.
Type test patterns. A type test pattern for type T is potentially applicable as follows:
- ref(S) && ref(T): if S is cast-convertible to T
- prim(S) || prim(T): if T == S, modulo boxing/unboxing
- val(S): if S == T, modulo boxing
- ref(S) && val(T): if S is cast-convertible to box(T)
If a pattern is not potentially applicable to the static type of the target, a compiler error ensues.
There are two possibly odd things here:
- Tight restrictions on when you can use primitive type-test patterns;
- Asymmetry between primitive and values (when we have them).
Both stem from the fact that primitive types have nontrivially overlapping value sets; type tests for "int x" and "long x" overlap, but type tests for named values never do.
If you want to ask whether an Object is an int, you can ask for a specific box type (or a shared supertype like Number) instead, either using a type-test pattern or a static destructuring pattern like Integer.valueOf(int x).
Destructuring patterns. Destructuring patterns for a type T (reference or value) have the same applicability restrictions as type-test patterns for T.
Constant patterns. Constant patterns have applicability restrictions as follows:
- String literals: if S is cast-convertible to String
- Enum literals (fully qualified): if S is cast-convertible to the enum type
- Enum literals (abbreviated): if S is the enum type
- Class literals: if S is cast-convertible to Class (we may wish to not support Class literals as a constant pattern, though)
- Numeric literals: if S is a primitive type or box, and the constant is a valid literal for that primitive type
- Boolean literals: if S cast-convertible to Boolean (though switches on boolean or Boolean should be disallowed; use if. Rationale to be provided separately.)
- Null literal: if S is a reference type
Note that all of this is not about "there are multiple kinds of switch", as much as statically eliminating silly combinations of patterns and targets based on typing.
> On Nov 4, 2017, at 4:55 PM, Mark Raynsford <mark at io7m.com> wrote:
> On 2017-11-03T21:30:17 +0100
> Remi Forax <forax at univ-mlv.fr> wrote:
>> I'm happy with choice #3 too.
> On 2017-11-03T15:37:20 -0400
> Brian Goetz <brian.goetz at oracle.com> wrote:
>> - There isn't really an overwhelming need for being able to say "Is
>> this Object a numeric zero" or "Is this object a boxed primitive in the
>> range of int."
> I agree. From my experience, despite the best efforts of autoboxing,
> many programmers treat primitives and references as being from entirely
> separate worlds. I'd therefore not really expect people to be matching
> on arbitrary things and relying on quiet primitive conversions behind
> the scenes to get the behaviour they want.
> This is obviously purely subjective!
> Mark Raynsford | http://www.io7m.com
More information about the amber-spec-observers