----- Mail original ----- > De: "Brian Goetz" <brian.go...@oracle.com> > À: "Remi Forax" <fo...@univ-mlv.fr> > Cc: "Guy Steele" <guy.ste...@oracle.com>, "Tagir Valeev" <amae...@gmail.com>, > "amber-spec-experts" > <amber-spec-experts@openjdk.java.net> > Envoyé: Dimanche 23 Août 2020 22:31:09 > Objet: Re: Letting the nulls flow (Was: Exhaustiveness)
>> Note that if we keep "default", it has to accept null because fundamentally, >> "default" is like "else". > > I agree on default, and I also agree on the comparison to `else`. Which > underscores the importance of totality here; a switch: > > case Frog f: > case Tadpole t: > default / case var x / case _ / case Object o / ... any of these, > they're all total > > is really equivalent to the if-else chain: > > if (x instanceof Frog f) { ... } > else if (x instanceof Tadpole t) { ... } > else { ... } > > ... *precisely because* the patterns in the last line are total. In > other words, a total pattern in a switch is like the `else` of an `if` > (and like with `if`, nothing can come after an unqualified `else` > clause, because it would be dead.) I believe this is the analogy you > are looking for in the comments above about refactoring between switch > and if chains; if the pattern is a "no op", when refactoring to/from an > if-else chain, the "no op" pattern maps to the else clause. yes > > (Note that we already see this nod to totality elsewhere in the > language, too; we distinguish between static casts, unchecked casts, and > dynamic casts, based on ... wait for it ... totality. If the "test" in > question is total, that affects the semantics of the "test", such as > what exceptions it may throw.) Not a good analogy because the fact that the compiler may remove the cast is actually crazy, by example, does this code raise an exception or not var list = (List<String>)(List<?>) List.of(3); // i've just an unsafe cast somewhere Object o = list.get(0); System.out.println(o); and same question with var o = list.get(0); The fact that a CCE can appear "randomly" is a big headache for my students. I will prefer not to repeat that mistake of the past. > >> For a destructuring pattern on Foo, if there is no "case Foo(null)", "case >> Foo(var x)" (or case var x at the top-level/or default), i don't see why this >> pattern has to accept "null" when destructuring because the switch will not >> know what to do with that null. Raising a NPE the earliest seems the right >> semantics for me. > > I am not sure exactly what you are saying here. > > case Foo(var x) > > always matches Foo(null), but it only matches `null` itself when > `Foo(var x)` is total on the target type (IOW, when the pattern test is > a no-op.) the sentence should have been i don't see why this pattern has to accept "null" as component > > >> I tried the usual jedi mind trick to convince you that a statement switch >> doesn't have to behave like a legacy switch but my force power doesn't seem >> to >> work through internet. > > It was a good try, you made me think for a few minutes. >> In that case, i suppose we can choose among the solutions proposed by Guy to >> get >> a statement switch which has no implicit "default". > > Let's be careful to separate the "optimistically total" feature (in the > presence of sealing) from the base switch semantics. I think the base > switch semantics are quite simple now -- we carve out legacy behavior > for three kinds of types (enums, strings, and boxes), and then switches > are 100% null-friendly after that. > > Scaling optimistic totality to sealed classes wrapped in deconstruction > patterns looks like it is going to require more work, but I think that's > a separate problem. I don't disagree. Rémi