Totality at switch statements

Hunor Szegi hunor.szegi at gmail.com
Tue Jun 21 10:02:21 UTC 2022


Thank you for the explanation. True, separating the data and the data
processing into different classes makes it more important to know about the
changes. A compile time error or a warning can show you the changes. But
using the default clause will eliminate this help. I can imagine an IDE
check, which shows you the uncovered cases, in spite there's a default
clause. But the best practice would be listing all the known types you are
aware of but for some reason don't want to process or can't accept.
(Hopefully using a concise syntax in the future.)

I still think so, forcing the totality of switch statements will result in
a lot of default clauses, which eliminates the totality check, and makes
some switches one line longer unnecessarily. But maybe I am wrong, and it
will help the developers to think about the pros and cons of this situation
and will result in better practices.

Regards,
Hunor

On Mon, 20 Jun 2022 at 15:26, Remi Forax <forax at univ-mlv.fr> wrote:

> Wow, one day to deliver an email, that's not fast :)
> I suppose it was stuck somewhere.
>
> Rémi
>
>
>
> ------------------------------
>
> *From: *"Hunor Szegi" <hunor.szegi at gmail.com>
> *To: *amber-dev at openjdk.org
> *Sent: *Sunday, June 19, 2022 12:42:12 AM
> *Subject: *Totality at switch statements
>
>
> Hi Hunor,
> thanks for your feedback.
>
> Let's start to understand why we want pattern matching, until now, if you
> want to describe an algorithm on a recursive data structure, you put the
> data and the parts of the algorithm in the same place ; you declare an
> interface with an abstract method corresponding to the algorithm and you
> implement the interface with different classes/records.
> In that case, the code of the algorithm is as important as the data, they
> are intermingled.
>
> Now, we want to introduce pattern matching, the idea of the pattern
> matching is to separate the data from the algorithm, the data are still
> defined using an interface and records, but the algorithm is not defined as
> an abstract method of the interface but as a method outside the data
> definition. You may ask why doing that, it's because the definition of the
> data is more important than the algorithm itself. Unlike with an abstract
> method which tied the data and the algorithm, here the data structure more
> important than the code itself.
>
> We have decided to express the pattern matching using switch, this means
> retrofitting the switch this new purpose.
> We have done that in several moves, first fix the semantics inherited from
> the C by adding the arrow syntax, then extends the semantics to allow
> switch expression and add patterns on types (type pattern and record
> patterns). All these moves as to be backward compatible so switch statement
> on int, String and enum must be still work as before.
>
> Totality is essential for pattern matching, it's the way to detect
> discrepancies between the definition of the data and the code of the
> pattern matching itself. Unlike with an abstract method, with pattern
> matching the data and the algorithm are defined in two different places so
> the data can be updated without the switch being updated. We want the
> compiler to fail in that case.
> So all the new switch constructs (switch on null, switch with guards,
> switch on patterns, switch expression) fails if the switch is not total,
> detecting when the data and the switch are not aligned anymore.
>
> This means that using "default" is now a blunder because while it makes
> the switch total it disable the compiler to warn the user when the data
> change.
> In the details, it's not fully true because we can switch on an infinity
> of values (or on a range big enough that it's like infinity in practice) so
> we can not enumerate all possibles combinations.
> Switches on ints and strings are in this category, so for them, there is
> no other solution than having a default. In the future a warning may be a
> good idea to ask for a default at the end of the switch.
> For the switch on enum, a user can enumerate all the cases but because of
> backward compatibility, we need a backward compatible way to opt-in to
> totality, my preferred way to do that is to add a "case null -> throw
> null", which transforms the old switch on an enum to a new switch on an
> enum which asks for totality.
>
> Now, to answer your questions, yes we want totality even in a switch
> statement to detect when the data change, default is kind of an
> anti-pattern so i don't see the need to have a special syntax for default
> -> {}, i prefer to keep the lambda syntax and the switch syntax aligned.
> Forcing totality on arrow syntax is something we have debated and we
> believe it's better to separate the two concerns: fix the semantics
> inherited from the C and totality, among other things it allows automatic
> conversion by tools between the colon syntax to the arrow syntax. Anyway,
> given that the arrow syntax is not a preview anymore, this option is not on
> the table anymore. About @total, you can use "case null -> throw null"
> instead. We may come with a true syntax later but not using an annotation
> because we have this rule that an annotation can not changed the semantics
> of the language.
>
> I hope this helps to understand why we wan totality,
> regards,
> Rémi
>
>
> Hi All,
>
> I really like the progress of adding pattern matching to Java (and a
> lot of other features). I can not wait to use it in productive code
> (non-preview). But I would like to give you feedback about a tiny detail,
> about the totality of switch statements.
>
> Surely, the totality is necessary at switch expressions, but forcing it at
> a statement is questionable.
>
> It can be helpful for the programmers to check the totality in those cases
> when the intent is that. But it is quite common to create a switch
> statement that doesn't handle all of the possibilities. Why would it be
> different using patterns? Why is it beneficial to force totality? This
> check can be an IDE feature. (Or the programmer can try adding a default
> clause, and the duplicated totality will be shown as an error. But I know
> it isn't convenient.)
>
> Honestly I feel that the rule, when the totality is forced, is dictated
> simply by the necessity of backward compatibility. What will happen if a
> new type (for example double) will be allowed for the selector expression?
> The backward compatibility wouldn't be an issue, but it would be weird
> behaving differently with an int compared to a double, so I guess the
> totality won't be forced. What would happen if the finality requirement was
> removed, and the equality could be checked for all types? What about the
> totality check in this imagined future?
>
> Of course the best would be to allow the totality check somehow if the
> intent is that. A warning could be a solution, but that can be annoying. It
> will force the programmers to suppress it, or to add a default clause (what
> is the current solution). Adding a default clause could be treated as
> marking the intent: we don't want totality. But the actual syntax does not
> represent it clearly.
>
> Additionally, there are issues with the "empty" default clause. In the JEP
> the "default: break;" was recommended, but interestingly it doesn't work
> with the arrow syntax. ("default -> break;" is a compile time error, only
> the "default: {break;}" is possible.) We can use both the "default: {}" and
> "default -> {}", which is fine. But while the "default:" is
> possible (without body), the "default ->" is an error. I don't know what is
> the reason behind it. Allowing an empty body with the arrow syntax would
> make the actual solution a little bit cleaner.
>
> I think forcing the totality at statements is partly an intent for
> uniformity. The best would be if the same rules were applied to all cases,
> regardless if it is a statement or expression, old or arrow syntax. But
> backward compatibility can't allow full uniformity, it moves the separation
> into the middle of a box, instead of a clear distinction between a
> statement and expression. It creates more cases.
>
> It would be possible, forcing the totality only at the arrow syntax. But
> that would move some of the programmers using the old syntax, which may be
> less fortunate.
>
> It would be possible to allow the programmer to mark the intended
> totality. Maybe a new keyword would be too much for this purpose.
> Alternatively I can imagine allowing annotations at a switch
> statement/expression in the future. For example:
>
> @total
> switch (x) {
> ...
> }
>
> If this syntax isn't possible, the annotation could be added right before
> the opening curly bracket. Alternatively a warning suppress annotation
> could be used specifically for the switch.
> But this is only a brainstorming. My main point is that I don't think the
> totality should be forced at switch statements. Or it should cause only a
> warning.
>
> I hope my feedback isn't pointless. I'd be very interested to hear other
> people's opinions.
>
> Thank you in advance,
>
> Regards,
> Hunor
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20220621/45c25eaa/attachment-0001.htm>


More information about the amber-dev mailing list