Like Tagir said, i'm not a fan of having two declarations for one variable too.
> 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. Technically, it's not fully LUB, because you have the precise re-throw, so it's more it's an union type but it's LUB when you call a method with the exception as argument or call a method on the exception. So we may be able to come with a similar semantics for the pattern A | B, this is an union type but it's LUB when ... . With the constraint that the semantics has to be retro-compatible in case of A and B are exceptions. > 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. yes, I don't think we have to worry too much in this case because as explained above once you do a method call, A | B is erased to LUB. So if we introduce a union type, it will be kept local to a method. Rémi > De: "Tagir Valeev" <amae...@gmail.com> > À: "Brian Goetz" <brian.go...@oracle.com> > Cc: "Remi Forax" <fo...@univ-mlv.fr>, "amber-spec-experts" > <amber-spec-experts@openjdk.java.net> > Envoyé: Samedi 10 Octobre 2020 05:24:27 > Objet: Re: instanceof and exceptions > Hello! > I don't like the idea of merging now, even though I advocated for it before. > First, it creates several declaration sites for the same variable. It will > complicate the navigation in IDE for users and for IDE authors as well. We > have > a similar case when the same method is declared in two interfaces and the user > wants to navigate from the call-site to the method declaration (in this case > IDE goes to one of the declarations). But in that case, there are actually two > super-methods. And here we have one variable with several declarations. > Second, > it could be really hard to understand the type of the variable because it > could > be scattered over many expressions. Moreover, this may cause accidental reuse > of the same name. And this opens a way for context-dependent types. E.g. > consider the statement: > if((obj instanceof A x && testA(x)) || (obj instanceof B x && testB(x))) > useAorB(x); > Here the type if `x` will depend on the place where it's used. I think we > don't > like to go to this direction. > That said, I like OR patterns. I think having a LUB type for the pattern > variable is completely ok for now. It's consistent with catch and useful > enough. In IntelliJ IDEA we have quite many code patterns that can benefit > from > this. E.g. (just did quick structural search through the codebase): > final PsiElement element = descriptor.getPsiElement(); > if (element instanceof PsiNewExpression || element instanceof > PsiArrayInitializerExpression) { > PsiReplacementUtil. replaceExpression ((PsiExpression)element, myEnumName + > ".values()" ); > } > Could be replaced with > if (descriptor.getPsiElement() instanceof (PsiNewExpression | > PsiArrayInitializerExpression) expression) { > PsiReplacementUtil. replaceExpression (expression, myEnumName + ".values()" ); > } > Or > Object normalized = value; > if (value instanceof Byte || value instanceof Short) { > normalized = ((Number)value).intValue(); > } > Could be replaced with > if (value instanceof (Byte | Short) number) { > normalized = number.intValue(); > } > And so on. This allows reducing the number of typecasts even further. > With best regards, > Tagir Valeev. > пт, 9 окт. 2020 г., 21:59 Brian Goetz < [ mailto:brian.go...@oracle.com | > brian.go...@oracle.com ] >: >> 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 >> | >> 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