> From: "Brian Goetz" <brian.go...@oracle.com>
> To: "amber-spec-experts" <amber-spec-experts@openjdk.java.net>
> Sent: Monday, February 28, 2022 5:50:25 PM
> Subject: Re: Primitive type patterns

> Let me put the central question here in the spotlight.

>> #### Boxing and unboxing

>> Suppose our match target is a box type, such as:

>> record IntegerBox(Integer i) { }

>> Clearly we can match it with:

>> case IntegerBox(Integer i):
> We could stop here, and say the pattern `int i` is not applicable to 
> `Integer`,
> which means that `IntegerBox(int i)` is not applicable to IntegerBox. This is 
> a
> legitimate choice (though, I think it would be a bad one, one that we would
> surely revisit sooner rather than later.)

> Users might well not understand why they could not say

> case IntegerBox(int i)

> because (a) you can unbox in other contexts and (b) this has a very clear
> meaning, and one analogous to `case ObjectBox(String s)` -- if the box 
> contents
> fits in a String, then match, otherwise don't. They might get even more balky
> if you could say

> int x = anInteger

> but not

> let int x = anInteger

> And, if we went with both Remi's preferences -- the minimalist matching 
> proposal
> and dropping "let" in the let/bind syntax (which we will NOT discuss further
> here) then the following would be ambiguous:

> int x = anInteger // is this an assignment, in which case we unbox, or a 
> match,
> in which case we don't?

> and we would have to "arbitrarily" pick the legacy interpretation.
I said the opposite, if you use "let" you do not have to support assignment 
conversions. 

> But all of this "balking" is symptom, not disease.

> The reality is that pattern matching is more primitive than unboxing
> conversions, and if we lean into that, things get simpler. An unboxing
> conversion may seem like one thing, but is actually two: try to match against 
> a
> partial pattern, and if there is no match, fail. In other words, unboxing is:

> int unbox(Integer n) ->
> switch(n) {
> case int i -> i;
> case null -> throw new NPE();
> }

> The unboxing we have jams together the pattern match and the throw-on-fail,
> because it has no choice; unboxing wants to be total, and there's no place to
> specify what to try if the pattern match fails. But pattern matching is
> inherently conditional, allowing us to build different constructs on it which
> handle failures differently.

> So *of course* there's an obvious definition of how `int x` matches against
> Integer, and its not a question of whether we "define" it that way, its a
> question of whether we expose the obvious meaning, or suppress it. I think the
> arguments in favor of suppression are pretty weak.
The strong argument is that instanceof/switch case is about subtyping 
relationship while assignment is about assignment conversions, trying to blur 
the lines between the two has already been tried in the past and it results in 
pain (see below). But i suppose every generations need to re-discover it. 

> There's a similar argument when it comes to narrowing from (say) long to int.
> There's a very natural interpretation of matching `int x` to a long: does the
> long value fit in an int. In assignment context, we do the best we currently
> can -- we allow the narrowing only if we can statically prove it will match,
> which mean if the match target is a constant. But again, conversions in
> assignment context are not the primitive. If we take the obvious definition of
> matching `int x` against long, then the current rules fall out naturally, and
> we can ask sensible questions like

> if (aLong instanceof byte) { ... }
> else if (aLong instanceof short) { ... }

> by *asking the primitive directly*, rather than appealing to some proxy (like
> manually unrolling the range check.)
What about ? 

Object o =... 
if (o instanceof byte) { ... } 

Does it means that o can be a Long ? 

RĂ©mi 

Reply via email to