Comparing ranges with switch
Red IO
redio.development at gmail.com
Fri Mar 15 10:00:36 UTC 2024
I misclicked and only replied to John.
I don't quite understand why we need to leave exhaustiveness on the table,
just because it's difficult to implement for custom types.
Also I don't think it's that complicated. Custom types just need open
ranges in the exhaustive patterns.
case foo.. ->
case ..bar ->
Sure you can't check if foo is correctly larger than than bar. But this
would need to be checked at runtime and throw an exception if the case. You
could easily implement custom ranges by requiring the type to be a
specialized iterable (that takes a starting point) and comparable. That way
you can iterate the range and choose the correct branch for range
comparisons.
Great regards
RedIODev
On Wed, Mar 13, 2024, 23:59 John Rose <john.r.rose at oracle.com> wrote:
> If we make range-comparison a non-primitive, via some sort of
> user-programmable pattern mechanism, we lose exhaustiveness.
>
> But we’d simplify the language, since we’d be “growing a language” (Guy’s
> YouTube talk!) via library code, not via adding to the spec. We’d get a
> switch like this:
>
> switch (b)
> {
>
> case Range.of(-128, -1) -> "It's negative!";
> case 0 -> "It's zero!";
> case Range.of(1, 127) -> "It's positive!";
>
> }
>
> With that approach, the coder needs a concluding case to force
> exhaustiveness:
>
> … default -> "It's positive!";
>
> or even
>
> … default -> throw AssertionError();
>
> That makes the patterns a little less compositional than a hardwired
> feature, but otherwise it’s a solid tradeoff.
>
> Hmm, in principle, it would also be possible to encode ranges in Java’s
> type system, enough to make some switches like this be exhaustive. (With
> the right library definitions.) The C2 JIT type system, used for
> optimizations, is fully aware of integer and long subrange types. (C2
> doesn’t do types representing multiple unconnected ranges, as of today.)
> That range-based type system, in fact, is how C2 would (after inlining)
> conclude that the above switch was in fact exhaustive. C2 would not bother
> to compile an unreached path to throw the impossible exception.
>
> That is, the JIT would properly optimize the switch as exhaustive, even if
> the static analysis of javac couldn’t quite get the job done. That’s a
> normal state of affairs: The JIT always knows more online than javac does
> offline.
>
> To be clear: I would never want to see ranges in the JLS (Java) type
> system. (IMO the volume of details would be overwhelming, with tricky rules
> for each operator, and all that for not enough gain.) Hence, if range
> patterns are user-defined, then exhaustiveness over ranged cases is
> probably something we have to leave off the table. The JIT would fix it
> under the covers, but there would be some warts in the source code (the
> default cases above).
>
> On 13 Mar 2024, at 10:40, Brian Goetz wrote:
>
> As an added bonus, it becomes practical to get useful exhaustiveness
> checking for integral types larger than boolean, as this example with byte
> shows.
>
> On 3/13/2024 12:33 PM, David Alayachew wrote:
>
> Well, what Brian and RedIO are suggesting would be more like this.
>
> byte b = 123;
>
> System.out.println
> (
> switch (b)
> {
>
> case -128..-1 -> "It's negative!";
> case 0 -> "It's zero!";
> case 1..127 -> "It's positive!";
>
> }
> )
> ;
>
> The difference is, in this example, it would be EXHAUSTIVE, NO DEFAULT
> CLAUSE NECESSARY. Your example requires either a default or a type pattern
> to be exhaustive, as do basically all switches that use when clauses.
>
> That exhaustiveness is powerful because -- patterns compose. For example,
> if you have a complex set of number checks, this switch will tell you if
> you left a value out. That is the power of exhaustiveness. When you combine
> it with composition, you can take an arbitrarily complex domain, and the
> compiler will let you know if you missed a case.
>
> On Wed, Mar 13, 2024 at 12:13 PM Josiah Noel <josiahnoel at gmail.com> wrote:
>
>> we sorta have this with switch guards
>>
>> switch (intyMcIntFace) {
>>
>> case Integer i when i > 1 && i < 10 -> {}
>>
>> case 42 -> {}
>>
>> default -> throw new IllegalArgumentException("Unexpected value: ");
>>
>> }
>>
>> On Wed, Mar 13, 2024 at 8:16 AM Red IO <redio.development at gmail.com>
>> wrote:
>>
>>> The switch statement saw a huge transformation over the past few
>>> releases. So I was quite surprised to realize that the current switch
>>> construct can't check the range of an value.
>>> Example case x is between y and z.
>>> I'm most likely not the first one to notice that. Is there any
>>> discussion about adding some sort of range pattern? Would fit in the notion
>>> of the switch checking patterns quite well.
>>>
>>> Great regards
>>> RedIODev
>>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20240315/5c51cbf7/attachment.htm>
More information about the amber-dev
mailing list