I guess the case I'm referring to is:

switch (lunch) {
        case Box(Soup s):
        case Bag(Soup s):
}

Where Soup is the only know subtype of Lunch. This code is exhaustive, but is Sandwich is added, it is no longer so.

Given Lunch is sealed to only permit Soup, and Container = Box|Bag, then this switch is exhaustive on COntainer<Lunch>.

Later, when Lunch is extended to permit Sandwich, this switch becomes not exhaustive on Container<Lunch>, and you get a compile error, as you would want.  Now you can totalize either by handling the sandwich-related cases, OR by adding Box(Lunch)/Box(var) cases, OR by adding Container(Soup) cases, OR by adding Container(Lunch) case, OR by adding a default/var case.

That said, if an error is issued whenever a switch statement is not exhaustive, that would alleviate my concerns - I think the error would cover this case also.

Yes.  Totality is compositional.  We can treat `Box(Soup)` as Box(var alpha) & alpha matches Soup.  Then the switch is a big disjunction of conjunctions, and we can rearrange with De Morgan's laws and related boolean algebra tricks.  So

   Box(Soup) OR Box(Sandwich)
   == (Box(var alpha) AND alpha matches Soup) OR (Box(var alpha) AND alpha matches Sandwich)
   == Box(var alpha) AND (alpha matches Soup OR alpha matches Sandwich)
   == Box(var alpha) AND (alpha matches Lunch)
   == Box(Lunch)

The think the I find mildly odd is that when Sandwich is added, it might not be enough to add another `case`. If there was null-related logic inside `case Box(Soup s):` that would have to be moved somewhere else. So while in most cases it will be a straightforward refactoring, I can see some puzzlers emerging here.

Can you pull on this string a big?

Reply via email to