In theory, patterns can be combined with AND and OR to produce new patterns, if 
their target types and binding lists are compatible.  Note also that most 
fallthroughs (those where the case labels immediately follow other case labels, 
with no intervening statements) can be expressed as OR patterns.  

Some form of OR patterns are almost a forced move if we want to have expression 
switches with patterns:

    int numLetters = switch(s) {
        case "one", "two" -> 3;
        case "three" -> 5;
        ...
    }

Because, while statement switches can simply repeat the labels:

    case "one":
    case "two":

this idiom looks pretty stupid if we try to transplant it to expression 
switches:

    case "one" ->
    case "two" -> wtf?

OR patterns give us much of what fallthrough gives us; the only difference is 
the ability to have intervening statements between the case labels.  Given that 
expression switches push us towards OR patterns, why not double down, using 
this for statement switches, and prohibit fallthrough for statement switches 
too?  This is simpler and covers what seem like the important cases.  

In theory, an OR pattern of P and Q would require that both P and Q are 
applicable to the static type of the target, and (in the most strict 
interpretation) have identical binding variable lists. 

Note that we have a form of OR patterns now, with multi-catch:

    catch (E1 | E2 identifier)

Though, this might not really be what we want an OR pattern to look like, as 
this looks like the OR of "E1" (no bindings) and "E2" (with bindings), which 
would fail our restriction on the binding variable lists being the same.  An OR 
pattern would more correctly be written (E1 e | E2 e).  (However, we could 
interpret “E1|E2 identifier” as a union type-test pattern if we wanted to unify 
catch with patterns.)

The big question is whether we need OR patterns at all, or whether this is 
merely an artifact of the switch statement.  For the matches expression, we can 
express ORs clearly enough without it:

    if (x matches P || x matches Q)

(and we need to support this anyway.)  If we used comma to separate patterns:

    case 1, 2, 3: 
    case Foo x, Bar x, Baz x:
    case Foo(var x), Bar(var x), Baz(var x): 

Is that clear enough?  Is that unambiguous enough?  If this works, this is nice 
because it works cleanly with existing constant switches too.  I think this is 
pretty good.  

So, concrete proposal:
 - Allow multiple patterns to be separated by commas in a case label;
 - Treat “case X: case Y:” as sugar for “case  X, Y:” in statement switches;
 - Impose the “same bindings” rule when multiple patterns are combined in this 
way;
 - Disallow fall through into patterns with binding variables. 

Note that we don’t have to create a new kind of switch here to prohibit fall 
through; we just don’t allow fall through into non-constant pattern cases.  

Note that Scala lets you OR multiple patterns together:

def matcher(l: Foo): String = {
  l match {
    case A() => "A"
    case B(_) | C(_) => "B"
    case _ => "default"
  }
}
but I'm not sure whether this is really an OR on patterns, or whether this is a 
"feature" of match?  But, this seems a pretty questionable syntax choice, as:

scala> 1 match {
     | case 1 | 2 => "one";
     | }
res0: String = one

scala> 1 | 2
res1: Int = 3
So, even though 1|2 is an integer constant whose value is 3, "case 1|2" is an 
OR pattern.  


Similarly, its even less clear that we need AND patterns.  Though I could 
imagine wanting intersection type-test patterns, like:

    switch (lambda) {
        case Predicate p && Serializable: ...
        case Predicate p: ...
    }

Are there compelling use cases for AND patterns that I’m missing?  


Reply via email to