On 28/04/2021 00:44, Remi Forax wrote:
------------------------------------------------------------------------
*De: *"Maurizio Cimadamore" <maurizio.cimadam...@oracle.com>
*À: *"Brian Goetz" <brian.go...@oracle.com>, "amber-spec-experts"
<amber-spec-experts@openjdk.java.net>
*Envoyé: *Mardi 27 Avril 2021 16:23:20
*Objet: *Re: Switch labels (null again), some tweaking
On 23/04/2021 16:38, Brian Goetz wrote:
So, I think the "a switch only accepts null if the letters
n-u-l-l are present", while a comforting move in the short
term, buys us relatively little, and dulls our pain receptors
which in turn makes it take way longer to learn how patterns
really work. I think we should go back to:
- A switch accepts null if (a) one of the case labels is
`null` or (b) the switch has a total pattern (which must
always be the last case.)
The proposal seems ok; it does nothing for the problem I'm
concerned about (e.g. type of the target expression changing and
influencing the totality analysis at a distance) - but that was
not address by the previous proposal either (as you say in your
email, admittedly, I was reading too much into your proposal).
At least, if you want a total type, you can make it explicit using
case var x
which is always total whatever the type switched upon.
So you have two tools to avoid influence at distance in the totality
analysis,
- for a switch where you can enumerate all the values enums, sealed
type (boolean should be in that category too IMO, but we did not agree
on that), if you list all the possible cases, a total case is not
required.
Without changes to switch, there is nothing which would break if a "new
case" is added (to the enum, or to the sealed hierarchy). So, listing
"all possible cases" relies on the assumption that you know what "all
cases" are. And right now there's no way to declaratively say: this
switch (statement) is gonna cover them all. Brian answer goes a bit in
that direction, which is good, for the outermost layer (but the problem
still exists somewhat at the nested level).
- use "var" so the total case is explicit, "case var x", "case
Foo(var x)", etc
Sure - this is a good solution. One minor annoying thing is that we
started from a position where we said "var is just for inference" and
now we're back in a place where, while var is still, conceptually at
least, about inference, developers will also be using it for totality
(as inference guarantees that you are always 100% covered).
Stepping back - my general feeling on this topic is that the
audience in this mailing list have a very intimate knowledge of
what a "total pattern" is, to the point that they are comfortable
building on top of this definition to e.g. define null behavior of
patterns. I'm a little afraid that the finer detail of totality
might be lost on the average Joe developer: totality is a much
more slippery concept than it seems. Sure, there is one obvious
way to make your pattern total: if the target expression has type
E, then add a type test pattern whose type is also E. That seems
easy enough. Except that, the type of E will not always be that
manifest in user code (e.g. might be the result of what javac
happens to infer on Tuesdays). And, if you mix this with sealed
classes, it might be possible for a switch to go from total to
non-total, as new permitted subtypes are added to a sealed
hierarchy. These might all be all corner cases - but I think it's
this complexity which contributes to my "isn't this all too
subtle?" feeling.
Obviously I'm well aware that nearly every path has been explored,
and no silver bullet seems to be laying around, so... this might
just be the best we can offer, and that's ok.
We tried to have a way to signal that a pattern is total or not, by
example, using a keyword, like total or default, but it did not work
well when a pattern is partially total like "case Foo(true, var x)"
Yep - I'm aware of the various roads not taken - I'm not suggesting that
there's some shiny great solution which is better than what is being
proposed; I just wanted to air my concerns about the complexity of the
programming model implied in this proposal. In other cases (DA/DU,
scoping of pattern variables and, to a certain degree type inference,
esp. after Java 8) the rules are mind-blowingly complex, but developers
can form an intuitive notion of how these things work, and trust the
compiler to do the right thing - e.g. these features, despite their
complexity are mostly out of your face, and when the compiler barks, the
user will have some idea of what went wrong (whooops, uninitialized
final variable here!). Totality analysis is not as complex as some of
the features mentioned above - but it is something that users will have
to think about in the back of their minds, when reasoning about how
their pattern switches work.
Maurizio
Cheers
Maurizio
Rémi