Draft JEP on Primitive types in patterns, instanceof, and switch

Brian Goetz brian.goetz at oracle.com
Mon Feb 13 18:57:40 UTC 2023



> I asked a whether the following would be legal in an earlier email.
> The draft now explains that it is, but I still wonder what this snippet
> would get desugared to.
> ```
> Number num = ...;
> if (num instanceof byte b) {
>      ...
> }
> ```

There are many choices of what the compiler could do.  If I had to 
guess, the most likely is an invocation of a library method like:

     static boolean isSafeToCastFromIntToByte(n) { ... }

followed by a cast to byte with `I2B` if this method returns true. This 
models the translation of the corresponding idiom with reference types; 
first check if the cast is safe with `INSTANCEOF` (which pushes a 
boolean on the stack), and then if that succeeds, use `CHECKCAST` to 
perform the casting.  Of course, the compiler could do the domain check 
inline as well; this is an implementation detail that the compiler has 
some latitude over.  Obviously there's also some latitude over where to 
put such helper methods and what to call them (as they may also be 
useful to other code, possibly because they capture snippets of language 
semantics that can be reused as as a method reference or method handle.)

> >>/I agree that revisiting concepts is something we have to do, but 
> breaking the difference between subtyping and primitive conversions 
> for a corner case, in isolation, is a decision hard to understand./
> >//
> >/First, a subtyping relationship for primitives does exist, though it 
> doesn’t cover all of the possible conversions./
> Second, nothing is broken, shattered, or wrecked even in cases subtyping doesn’t exist, because the draft doesn’t propose to *change* the meaning of patterns, but rather to generalise them. I.e. its insight is that subtyping is a special case of assignment conversion, and so patterns are “really” about the validity of such conversions rather than about the specific case of subtyping.
> I had a quick look over the spec and I couldn't find any place that
> actually used the subtyping relation between primitives. The case
> you seem to be claiming widening primitive conversion matches 100%
> with the rules about subtyping, it actually doesn't use subtyping
> at all and just lists all possible conversions.

I don't think Ron is claiming that; the bit about "Well, there actually 
is subtyping here" was offered to rebut a spurious claim to the 
contrary.  But I don't think this is that important; whether int <: long 
or not is not that relevant to the language (personally I think the spec 
would be better without trying to define subtyping on primitives, for 
exactly the sorts of confusions that have come up in this thread, such 
as covariant overrides), and primitive widening conversions do not align 
with subtyping (because some widenings are not unconditionally exact.)

But, all of this is a distraction.  The question is whether the sorts of 
unsafety you get from casting between reference types (it might throw 
NPE) and the sorts of unsafety you get from casting between primitives 
(it might succeed but give you a garbage value) are so radically 
different that attempting to unify them under the same rubric (check 
instanceof before you cast) is some sort of Lovecraftian horror.  The 
reality is, we have gotten used to them being separate, but they're not 
really that different.
> A interesting question that could be asked is:
> "What does subtype/supertype mean". Although it is used in the spec
> it isn't really explain apart from being a reflexive and transitive
> relationship between types.

And if you pull on this string some more, you get to "what do types 
really mean."  Which is an excellent philosophical question, one which 
has been studied extensively with multiple overlapping theories (e.g., 
intensional vs extensional) that are useful in different situations.  
Sometimes it is useful to think of types as sets of values, where 
subtyping is mere subsetting; in other situations, it is useful to think 
of them as objects in a category.

But if you do pull on this string, you realize that it does not really 
offer much of an answer here, because people form their own theories 
about what types and subtyping are (which can be useful!) -- and then 
some language designer comes along and invalidates those personal 
theories by evolving the language in a way the theory didn't 
anticipate.  And this is where the strong emotional reaction of "but 
THATS not what instanceof (or whatever) means" comes from, which is 
mostly what we're dealing with here.

And, I realize as a language designer, one of the things I should be 
aware of is how much we are asking users to change their mental models.  
And this too is a balance; making the language more expressive or 
consistent always risks stepping on someone's mental model.  We do ask 
ourselves "how much will this freak people out" (and sometimes the 
answer is "way too much, we can't do that"), but this is but one input 
into the balance.


> A few of the emails argue that we should see `instanceof` as a
> safe cast check, not like an "inheritance" check or
> a "reference subtype" check. If the operator was called
> `canbesafelyconvertedto` I might agree, but thats not the case.

Yes, past syntax decisions affect our mental models, and sometimes a 
past choice is so constraining that there is no room to extend an 
existing syntax, but this is surely a subjective business.  But I agree 
with you that this is at the heart of the most significant negative 
reactions to this JEP -- people have associated specific and personal 
feelings about the meaning of the term "instanceof". But we have to 
realize these are personal associations, and while they may work for us, 
we should be aware that these are our own personal associations.

> Now this might seem I'm arguing in favour of `instanceof byte`.
> And on it's own that might look correct, but then you have
> justify the fact that the integer values/instances between -128
> and 127 and the byte values/instances are the same. In my mind
> the way this would be justified is by using the fact that `byte`
> is a subtype of `int` which is why it's set of instances is a
> subset of the instances of `int`. But this raises the issue
> that the set of instances of `int` can't be a subset of the set
> of instances of `float` cause not all ints are valid floats.

Again, you have developed a mental model of what a type is, and what 
instanceof means, that has been useful to you.  Yours is very much tied 
to subtyping, which is consistent with the current model. Which is fine 
-- but it's just a mental model.  I think you'll find that after you get 
over the frustration of "the world changed", snapping to this 
generalization of instanceof won't actually be that hard.  Give it some 
time.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20230213/74dc75ce/attachment.htm>


More information about the amber-dev mailing list