Letting the nulls flow (Was: Exhaustiveness)

forax at univ-mlv.fr forax at univ-mlv.fr
Sun Aug 23 21:10:41 UTC 2020


----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Guy Steele" <guy.steele at oracle.com>, "Tagir Valeev" <amaembo at gmail.com>, "amber-spec-experts"
> <amber-spec-experts at 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


More information about the amber-spec-experts mailing list