switch: using an expicit type as total is dangerous
forax at univ-mlv.fr
forax at univ-mlv.fr
Tue Sep 1 00:54:46 UTC 2020
----- Mail original -----
> De: "daniel smith" <daniel.smith at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Brian Goetz" <brian.goetz at oracle.com>, "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Mardi 1 Septembre 2020 02:16:47
> Objet: Re: switch: using an expicit type as total is dangerous
>> On Aug 30, 2020, at 5:37 AM, forax at univ-mlv.fr wrote:
>>
>> Ok, let's take an example, i've written a method getLiteral()
>> Number getLiteral(String token) {
>> if (token.equals("null")) {
>> return null; // null is part of the domain
>> }
>> try {
>> return Integer.parseInt(token);
>> } catch(NumberFormatException e) {
>> return Double.parseDouble(token);
>> }
>> }
>>
>> and a statement switch in another package/module
>> switch(getLiteral(token)) {
>> case Integer -> System.out.println("Integer");
>> case Double -> System.out.println("Double");
>> case Number -> System.out.println("null");
>> }
>>
>> but now i change getLiteral() to add string literal
>> Object getLiteral(String token) {
>> if (token.equals("null")) {
>> return null; // null is part of the domain
>> }
>> if (token.startsWith("\"") {
>> return token.substring(1, token.length() - 1);
>> }
>> try {
>> return Integer.parseInt(token);
>> } catch(NumberFormatException e) {
>> return Double.parseDouble(token);
>> }
>> }
>>
>> If i only recompile getLiteral(), and run the code containing the switch, i get
>> a ICCE at runtime because the signature of getLiteral() has changed, which is
>> good,
>> but if i now recompile the switch, the code compiles without any error but with
>> a different semantics, duh ?
>
> One thing to notice is that, depending on context, the real bug report coming
> out of this sequence of events is quite likely to have the form "The
> Widget.poke method doesn't behave correctly when operating on a string." The
> subtle difference in the treatment of 'null' may be relatively obscure.
It is obscure because null is usually not part of the domain so it will take a long time to debug this kind of issue.
>
> Anyway, what I think you're really after is a way for the programmer to assert
> that silently falling out of this switch is unexpected. And 'sealed switch'
> (however expressed syntactically) is the tool you need to do that. (Let's not
> dive into aspects of that feature here, though, there's another thread.)
yes, it's one solution, the other is to always use 'var' when what you want is a total pattern.
>
> Another thing you may appreciate is a warning or error if a non-sealed switch
> has a total case, because if the switch is total, it's very likely expected to
> be total forever. We could do this with optimistically-total enum switches,
> too. I don't think I'd want to do it with 'default' switches, which can be
> thought of as the "legacy" version of 'sealed switch'.
It's more or less what a part of what i've proposed in another thread,
i propose to emit a warning even if there is a default.
Combined with the fact that you can declare a total switch means that you can suggest to users to retrofit the existing enum switch (and uses of the new switch on types) to a total switch.
>
> (Whether this is a code-style-enforcing 'lint' warning or a compiler error
> probably comes down to a cost-benefit analysis. "How likely is the switch
> operand type to change?" vs. "How annoyed will programmers be that we're making
> them add 'sealed' or rewrite to 'default'?")
If IDEs provide this an automatic refactoring, the cost of changing the code may be greatly reduced.
Rémi
More information about the amber-spec-experts
mailing list