instanceof and exceptions
forax at univ-mlv.fr
forax at univ-mlv.fr
Sat Oct 10 11:10:10 UTC 2020
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" <amaembo at gmail.com>
> À: "Brian Goetz" <brian.goetz at oracle.com>
> Cc: "Remi Forax" <forax at univ-mlv.fr>, "amber-spec-experts"
> <amber-spec-experts at 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.goetz at oracle.com |
> brian.goetz at 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20201010/28188f3a/attachment.htm>
More information about the amber-spec-experts
mailing list