Draft JEP on Primitive types in patterns, instanceof, and switch
Angelos Bimpoudis
angelos.bimpoudis at oracle.com
Tue Feb 14 22:43:39 UTC 2023
Hello all,
A new version of the draft JEP on primitive types in patterns, instanceof, and switch is online:
https://openjdk.org/jeps/8288476
Many thanks,
Angelos
________________________________
From: amber-dev <amber-dev-retn at openjdk.org> on behalf of Brian Goetz <brian.goetz at oracle.com>
Sent: 13 February 2023 19:57
To: Robbe Pincket <robbepincket at live.be>; amber-dev <amber-dev at openjdk.org>
Subject: Re: Draft JEP on Primitive types in patterns, instanceof, and switch
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/20230214/5a4bb91b/attachment-0001.htm>
More information about the amber-dev
mailing list