There's a simple (though unsatisfying) answer to your student's question: because catch is special.  (But this answer belies an important point: ad-hoc syntactic "patches" like multi-catch may be satisfying in the short term, but they always beget a flurry of "for consistency" arguments, which can often not be consistently satisfied.)

Essentially, you are asking whether (a) OR patterns make sense, and (b) whether we can reuse the syntax A|B for OR'ed type patterns.  Let's pull on that string for a bit.

Suppose that `(T|U) tu` were a type pattern.  The type test is clear enough, but what is the type of `tu`?  There are two candidates; a union type and LUB.  (Union types are clearly more general, but what does multi-catch do?  It does LUB.  See JLS 14.20.)  I suspect your preferred answer is "union type", since that's a more information-preserving answer.  One downside here is it provides a(nother) vector for non-denotable weird types to escape to where users can observe them.  (We faced a similar issue in Lambda (and LVTI): both allowed intersection types to escape more freely into the wild, and both had consequences.) It's a possibility, but I'm not sure it clears the bar for risk/reward.

A more general answer that doesn't involve introducing new syntactic forms into the language would be to dust off the proposal for binding variable merging:

    if (x instanceof RuntimeException e || x instanceof Error e) { X }

Our original design allowed for this but we backed off because it was unclear whether the return-on-complexity was justified. Here, we'd see that we have two patterns, each with a binding of the same name, and exactly one of which can produce a binding at X.  So we can give `e` either a LUB or union type, just as with the "shortcut" T|U, but its a more general solution, and doesn't tease users with a "fake" union type syntax.

Note that the "merging" situation has another analogue with the same semantics: fallthrough.  The above is like:

    switch (x) {
        case RuntimeException e:
        case Error e:
            X;
    }

If fake union types are not great, what about real union types? Well, it's been shown to be possible (https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.158.1253&rep=rep1&type=pdf), but its a lot; it seems like the cost-benefit isn't there either.

Overall, I'm skeptical that the cost/benefit of union type patterns is positive, but they are doable.





On 10/9/2020 6:11 AM, Remi Forax wrote:
Following the course on exceptions, where i explain that a catch() is an 
instanceof,
two different students ask me why catch() can use '|' in between the exception 
types but instanceof can not.

i.e why this code works
   try {
     ...
   } catch(RuntimeException | Error e) {
     throw e;
   } catch(Throwable t) {
     ...
   }

but this one doesn't
   try {
     ...
   } catch(Throwable t) {
     if (t instanceof RuntimeException | Error e) {
       throw e;
     }
     ...
   }

I wonder if people will want to do pattern matching on exceptions ?

Rémi

Reply via email to