Let's say I start with this:


switch (lunch) {
        case Box(Soup s) {
             if (s == null) {
                  System.err.println("Box of null");
             } else {
                  System.err.println("Box of soup");
             }
        }

        case Bag(Soup s): {
             if (s == null) {
                  System.err.println("Bag of null");
             } else {
                  System.err.println("Bag of soup");
             }

        }

}


I assume that you are saying Box permits Soup only.  But your assumptions about "where do the nulls go" here are not right. Box(Soup) does not match Box(null); the set of patterns { Box(Soup) } is total on Box(Lunch) _with remainder Box(null)_.  So the null paths in this example are dead.  (Also missing break statements.)  So rewriting, this switch is really equivalent to:

    switch (lunch) {
        case Box(Soup s):
              System.err.println("Box of soup");
              break;

        case Bag(Soup s):
             System.err.println("Bag of soup");
             break;

        /* implicit */
        case Box(null), Bag(null): throw new NPE();
    }

and the switch is total on Container(Lunch) under the Lunch=Soup, Container=Box|Bag assumptions.


Then Sandwich is added to the hierarchy. The switch no longer compiles, I have to make it total. The following, which is, I believe, the first thing that will come to mind:

switch (lunch) {
        case Box(Soup s) {  /* same as before */ }
        case Box(Sandwich s) { ... }
        case Bag(Soup s): { /* same as before */ }
        case Bag(Sandwich s) { ... }
}


With the correct version of the first switch, your first idea for fixing it to accomodate sandwiches is correct!  The switch is now total on Container<Lunch>, the old cases handle the same thing they did, the new cases handle the new cases, and the implicit cases (Box(null) etc) handle the same thing they did.

But if it is considered exhaustive, then this will compile, but the null handling logic will be in the wrong place, and will be essentially dead code (which the user might be unaware of).

No, because it was dead in the first place too.  Box(null) and Bag(null) were always treated as remainder.


Reply via email to