RFR: 8279986: methods Math::asXExact for safely checked primitive casts

Brian Goetz brian.goetz at oracle.com
Wed May 25 15:12:02 UTC 2022


> Themotivation behind this PR isnot driven by pattern matching:John 
> Rose (the reporter of the JBS issue) is concerned about having safer 
> casts that, in addition, can be JITted to efficient code.
>

Yes, of course.  But also, as the platform evolves, it often happens 
that the same issues arises in separate places.  It would be terrible if 
asXExact had a different opinion of what could be converted exactly to 
an int, from the language's `instanceof int`. And similarly, it would be 
unfortunate if the semantics were driven by nothing more than "who got 
there first."  So we frequently discover things that we thought were 
independent increments of functionality, that turn out to want to be 
co-designed with other things, even those not known at the time we 
started.  This is just how this game works.

> But I get your point that having non-throwing testsbetter serves 
> pattern matching.However, I’m not sure that pattern matching alone 
> would remove the more general need for the proposed methods.
>

Yes, not sure either.  The game here is "find the primitive"; our first 
move in this game is not always the right one.  So let's figure it out.

> As for the controversial question about -0.0, as you note any proposal 
> will kind of suck. With “safer” cast methods we can have two (or even 
> more) variants.
>

Also, small changes in terminology can subtly bias our answer. Words 
like "exact", "information-preserving", "loss of precision", "safe", 
etc, all seem to circle the same concept, but the exact choice of 
terminology -- often made at random at the beginning of an investigation 
-- can bias us one way or the other.  (Concrete illustration: converting 
-0.0 to 0 seems questionable when your rule is "no loss of information", 
but seems more ambiguous when your rule is "safe".)

> In the context of primitive pattern matching (including the relevant 
> material in the JLS), however, it would be probably much simpler to 
> allow for matches between integral types on one side and for matches 
> between floating-point types on the other side, but not a mix. The 
> nuisance with -0.0 and other special values would disappear altogether.
>

That was my first thought as well, but then I had second thoughts, as 
this seemed like wishful thinking.  If we are willing to say:

     long x = 7;
     if (x instanceof int) { /* yes, it is */ }

then it may seem quite odd to not also say

     double x = 7.0;
     if (x instanceof int) { /* this too? */ }

Because, the natural way to interpret the first is "is the value on the 
left representable in the type on the right."  Clearly 7 is 
representable as an int.  But so is 7.0; we lose nothing going from 
double 7.0 -> int 7 -> double 7.0.  And whoosh, now we're sucked into 
belly of the machine, staring down -0.0 and NaN other abominations, 
questioning our life choices.

That's not to say that your initial thought is wrong, just that it will 
be surprising if we go that way.  Maybe surprises are inevitable; maybe 
this is the least bad of possible surprises.  We should eliminate the 
"maybes" from this analysis first, though, before deciding.

> Thus, while examples like
>
>     if (anIntExpression instanceof byte b)
>
> and
>
>     if (aDoubleExpression instanceof float f)
>
> make perfect sense, would an example like
>     if (aDoubleExpression instanceof short s)
>
> be pragmatically reasonable?
>
> IIRC, the discussions about “Primitive type patterns” and “Evolving 
> past reference type patterns” in the amber-spec-experts mailing list 
> of April 2022 don’t even mention the mixed integral/floating-point case.
>

Correct, we hadn't gotten there yet, we were still trying to wrap our 
heads around how it should work in the easier cases.



More information about the core-libs-dev mailing list