[External] : Re: Final variable initialization problem with exhaustive switch
Dimitris Paltatzidis
dcrystalmails at gmail.com
Wed Dec 1 14:31:36 UTC 2021
The title of this thread is probably just the tip of the iceberg at this
point..
Little I know, I was "surprised" to find out that I could not initialize
the final variable in a seemingly exhaustive switch.
The problem here was my assumptions about the switches. If we overlook the
fact that switch expressions return values and we don't account the default
keyword,
I thought: switch expressions ⊆ switch statements. Of course, we can have
less cases in a statement, while in a expression we must have the full
number, so that
contributes towards the statements being a superset of expressions, but at
the same time, with all the cases, the expressions can give us more than
what
statements can. And that's the "problem". All the compiler sees in a switch
statement is reduced to:
if (case1) {
} else if (case2) {
.
.
} else if (caseN) { //caseN happens to be the last possible case, but how
can we know that without any context?
}
Can you guarantee that there will be 1 branch that will get executed in the
above if statement, without any context? I can't, and neither does the
compiler.
While, reducing a switch expression we have:
if (case1) {
} else if (case2) {
.
.
} else if (caseN) {
} else { //Hidden
throw
}
Now, we can guarantee.
Of course switch statements can't have that, because that will break
people's code. So again, ignoring the default keyword, what we have now, is:
1. Switch expressions, things that return a value and are exhaustive.
2. Switch statements, things that execute your code, but are inherently
partial no matter what. (yes, even if every possible case is present)
To me, something is missing:
3. Total switch expressions, things that execute your code and are
exhaustive.
A suggestion made by others, stated by John:
> by allowing the switch to accept an extra keyword to say whether it is
> supposed to be exhaustive.
>
So why can't switch statements enjoy the totality of expressions too?
enum Parity {ODD, EVEN}
final int a;
total switch (p) {
case Parity.ODD -> a = 1;
case Parity.EVEN -> a = 0;
}
int b = a + 1; //Compiles
Now: switch expressions ⊆ (switch statements ∪ total switch statements). Of
course switch expressions are total by default:
final int a = total switch (p) { //The total here is redundant
case Parity.ODD -> a = 1;
case Parity.EVEN -> a = 0;
};
int b = a + 1;
Having partial switch statements was a bad decision in the first place? Now
we have them, and as Brian said, we can't change that, but that shouldn't
stop us
from adding total switch statements. Maybe trying to sculpt partial into
total is the problem here, instead maybe they should be 2 distinct entities.
Of course let's not get ahead of ourselves. As John said:
> these fixes is they feel like a complexity tax on the language for very little expressive gain.
>
> There could be people seeing the above parity total-switch statement and
wonder why? "It's clearly exhaustive, why add the total keyword?
What's the point of that keyword anyway?"
And that just adds more confusion, at least for now.
For the total suggestion to work, people should have in mind the model
that, switch statements are just a fancy way for a bunch of if and else if
statements.
If they want more (exhaustiveness), totality is the way.
But again, maybe that just feels like trying to educate them.
Στις Τετ, 1 Δεκ 2021 στις 6:22 π.μ., ο/η John Rose <john.r.rose at oracle.com>
έγραψε:
>
>
> > On Nov 30, 2021, at 8:06 AM, Remi Forax <forax at univ-mlv.fr> wrote:
> >
> > Fortunately, a warning does not break the code.
>
> That would help. Such a warning would note where there are two good
> answers and the language has to pick one but the user might well be
> thinking of the other.
>
> There should be ways to fix the warning very simply and without excess
> noise. For the legacy behavior I think this line works:
>
> default -> break;
>
> There is no correspondingly simple way to ask for an exhaustiveness check
> but some have suggested in the past this new syntax:
>
> default -> throw;
>
> The meaning of a lone throw would be “make a suitable exception for this
> spot in thhis switch and throw it”. Defining suitable is tricky since it is
> a DWIM request.
>
> Others have suggested putting the explicit selection at the top, by
> allowing the switch to accept an extra keyword to say whether it is
> supposed to be exhaustive.
>
> The problem with any of these fixes is they feel like a complexity tax on
> the language for very little expressive gain.
More information about the amber-dev
mailing list