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