From manoj.palat at in.ibm.com Wed Jul 14 22:43:40 2021 From: manoj.palat at in.ibm.com (Manoj Palat) Date: Wed, 14 Jul 2021 22:43:40 +0000 Subject: case null and type pattern Message-ID: Hi Gavin, All, In http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1, Section 14.11, I see "If a switch label has a null case label element then if the switch label also has any pattern case element labels, they must be type patterns (14.30.1)." implying that the following code: private static void foo(Object o) { switch (o) { case null, Integer i && i > 10 -> System.out.println(0); // flag error? default -> System.out.println(o); } should have an error flagged for case label with null since the pattern case label element is not a type pattern but a guarded pattern. Any reason for excluding guarded patterns here? Or does this requires a modification in spec to include guarded patterns as well? Regards, Manoj From forax at univ-mlv.fr Sun Jul 18 17:34:27 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 18 Jul 2021 19:34:27 +0200 (CEST) Subject: case null and type pattern In-Reply-To: References: Message-ID: <346859106.49728.1626629667064.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Manoj Palat" > To: "amber-spec-experts" > Sent: Jeudi 15 Juillet 2021 00:43:40 > Subject: case null and type pattern > Hi Gavin, All, HI Manoj, > > In > http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1, > Section 14.11, I see "If a switch label has a null case label element then if > the switch label also has any pattern case element labels, they must be type > patterns (14.30.1)." implying that the following code: > > private static void foo(Object o) { > switch (o) { > case null, Integer i && i > 10 -> System.out.println(0); // flag error? > default -> System.out.println(o); > } > should have an error flagged for case label with null since the pattern case > label element is not a type pattern but a guarded pattern. > Any reason for excluding guarded patterns here? Or does this requires a > modification in spec to include guarded patterns as well? For me, a guarded pattern or a parenthesis pattern are kind of composite patterns, you need to take a look at what's inside. But I'm letting Gavin answer to your specific questions. > > Regards, > Manoj regards, R?mi From gavin.bierman at oracle.com Mon Jul 19 10:16:11 2021 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 19 Jul 2021 10:16:11 +0000 Subject: case null and type pattern In-Reply-To: References: Message-ID: <8E0D6D09-E4E6-4D7C-AC2D-CBE71727BD50@oracle.com> Hi Manoj, This is certainly something we can discuss for Preview 2, but?it is intentional for now. The `case null, T t` is really a special case of extending a type pattern to be null friendly. Remember that the pattern variable `t` is initialised with null if this case label applies. The problem with what you are suggesting is that we?re going to end up with code like: switch(o) { ? case null, String s && s != null -> ? } Now the developer has a guard saying that `s` is not null, and is probably going to assume this in the body of the switch rule, but has erroneously extended the case label with null. So, it?s just an attempt to remove potential puzzlers by design. But we can take a look at whether we can capture a more generous definition. An alternative would be to define a new null-friendly type pattern. Let?s talk about it! Thanks, Gavin > On 14 Jul 2021, at 23:43, Manoj Palat wrote: > > Hi Gavin, All, > > In http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1, Section 14.11, I see "If a switch label has a null case label element then if the switch label also has any pattern case element labels, they must be type patterns (14.30.1)." implying that the following code: > > private static void foo(Object o) { > switch (o) { > case null, Integer i && i > 10 -> System.out.println(0); // flag error? > default -> System.out.println(o); > } > should have an error flagged for case label with null since the pattern case label element is not a type pattern but a guarded pattern. > Any reason for excluding guarded patterns here? Or does this requires a modification in spec to include guarded patterns as well? > > Regards, > Manoj From brian.goetz at oracle.com Mon Jul 19 13:00:16 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Jul 2021 09:00:16 -0400 Subject: case null and type pattern In-Reply-To: <8E0D6D09-E4E6-4D7C-AC2D-CBE71727BD50@oracle.com> References: <8E0D6D09-E4E6-4D7C-AC2D-CBE71727BD50@oracle.com> Message-ID: <66a6e6ec-bd3c-2e4c-a7c8-a7067a50fb0a@oracle.com> Right.? In theory, we could allow guarded patterns here, but they will be hard to use, since users will likely want to use the binding in the guard, and would have to check for nullity every time. On 7/19/2021 6:16 AM, Gavin Bierman wrote: > Hi Manoj, > > This is certainly something we can discuss for Preview 2, but?it is intentional for now. The `case null, T t` is really a special case of extending a type pattern to be null friendly. Remember that the pattern variable `t` is initialised with null if this case label applies. > > The problem with what you are suggesting is that we?re going to end up with code like: > > switch(o) { > ? > case null, String s && s != null -> ? > } > > Now the developer has a guard saying that `s` is not null, and is probably going to assume this in the body of the switch rule, but has erroneously extended the case label with null. > > So, it?s just an attempt to remove potential puzzlers by design. But we can take a look at whether we can capture a more generous definition. An alternative would be to define a new null-friendly type pattern. Let?s talk about it! > > Thanks, > Gavin > > > >> On 14 Jul 2021, at 23:43, Manoj Palat wrote: >> >> Hi Gavin, All, >> >> In http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1, Section 14.11, I see "If a switch label has a null case label element then if the switch label also has any pattern case element labels, they must be type patterns (14.30.1)." implying that the following code: >> >> private static void foo(Object o) { >> switch (o) { >> case null, Integer i && i > 10 -> System.out.println(0); // flag error? >> default -> System.out.println(o); >> } >> should have an error flagged for case label with null since the pattern case label element is not a type pattern but a guarded pattern. >> Any reason for excluding guarded patterns here? Or does this requires a modification in spec to include guarded patterns as well? >> >> Regards, >> Manoj -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jul 19 13:09:52 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 19 Jul 2021 15:09:52 +0200 (CEST) Subject: case null and type pattern In-Reply-To: <66a6e6ec-bd3c-2e4c-a7c8-a7067a50fb0a@oracle.com> References: <8E0D6D09-E4E6-4D7C-AC2D-CBE71727BD50@oracle.com> <66a6e6ec-bd3c-2e4c-a7c8-a7067a50fb0a@oracle.com> Message-ID: <2071220884.417222.1626700191999.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Gavin Bierman" , "Manoj Palat" > > Cc: "amber-spec-experts" > Sent: Lundi 19 Juillet 2021 15:00:16 > Subject: Re: case null and type pattern > Right. In theory, we could allow guarded patterns here, but they will be hard to > use, since users will likely want to use the binding in the guard, and would > have to check for nullity every time. As you said, the error is to be able to use the binding not to be able to express that pattern. R?mi > On 7/19/2021 6:16 AM, Gavin Bierman wrote: >> Hi Manoj, >> This is certainly something we can discuss for Preview 2, but?it is intentional >> for now. The `case null, T t` is really a special case of extending a type >> pattern to be null friendly. Remember that the pattern variable `t` is >> initialised with null if this case label applies. >> The problem with what you are suggesting is that we?re going to end up with code >> like: >> switch(o) { >> ? >> case null, String s && s != null -> ? >> } >> Now the developer has a guard saying that `s` is not null, and is probably going >> to assume this in the body of the switch rule, but has erroneously extended the >> case label with null. >> So, it?s just an attempt to remove potential puzzlers by design. But we can take >> a look at whether we can capture a more generous definition. An alternative >> would be to define a new null-friendly type pattern. Let?s talk about it! >> Thanks, >> Gavin >>> On 14 Jul 2021, at 23:43, Manoj Palat [ mailto:manoj.palat at in.ibm.com | >>> ] wrote: >>> Hi Gavin, All, >>> In [ >>> http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1 >>> | >>> http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1 >>> ] , Section 14.11, I see "If a switch label has a null case label element then >>> if the switch label also has any pattern case element labels, they must be type >>> patterns (14.30.1)." implying that the following code: >>> private static void foo(Object o) { >>> switch (o) { >>> case null, Integer i && i > 10 -> System.out.println(0); // flag error? >>> default -> System.out.println(o); >>> } >>> should have an error flagged for case label with null since the pattern case >>> label element is not a type pattern but a guarded pattern. >>> Any reason for excluding guarded patterns here? Or does this requires a >>> modification in spec to include guarded patterns as well? >>> Regards, >>> Manoj -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jul 19 13:49:22 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 19 Jul 2021 09:49:22 -0400 Subject: [External] : Re: case null and type pattern In-Reply-To: <2071220884.417222.1626700191999.JavaMail.zimbra@u-pem.fr> References: <8E0D6D09-E4E6-4D7C-AC2D-CBE71727BD50@oracle.com> <66a6e6ec-bd3c-2e4c-a7c8-a7067a50fb0a@oracle.com> <2071220884.417222.1626700191999.JavaMail.zimbra@u-pem.fr> Message-ID: <812efdc9-ba93-0271-f772-3ce24861e9de@oracle.com> Sure, but again, I think users would find that restriction even more annoying; the vast majority of guards in languages with guarded patterns use the bindings to refine the match.? I'm not sure users would thank us for moving the restriction in this way. A more interesting flavor of the same question, though, is what happens if we allow _ to be used as a binding name.? Then, the restriction on fall-through could be relaxed if we wanted; it would be safe to do: ??? case Foo _, Bar _: blah whereas today we don't allow mixing of these patterns. On 7/19/2021 9:09 AM, Remi Forax wrote: > > > ------------------------------------------------------------------------ > > *From: *"Brian Goetz" > *To: *"Gavin Bierman" , "Manoj Palat" > > *Cc: *"amber-spec-experts" > *Sent: *Lundi 19 Juillet 2021 15:00:16 > *Subject: *Re: case null and type pattern > > Right.? In theory, we could allow guarded patterns here, but they > will be hard to use, since users will likely want to use the > binding in the guard, and would have to check for nullity every time. > > > As you said, the error is to be able to use the binding not to be able > to express that pattern. > > R?mi > > > > On 7/19/2021 6:16 AM, Gavin Bierman wrote: > > Hi Manoj, > > This is certainly something we can discuss for Preview 2, but?it is intentional for now. The `case null, T t` is really a special case of extending a type pattern to be null friendly. Remember that the pattern variable `t` is initialised with null if this case label applies. > > The problem with what you are suggesting is that we?re going to end up with code like: > > switch(o) { > ? > case null, String s && s != null -> ? > } > > Now the developer has a guard saying that `s` is not null, and is probably going to assume this in the body of the switch rule, but has erroneously extended the case label with null. > > So, it?s just an attempt to remove potential puzzlers by design. But we can take a look at whether we can capture a more generous definition. An alternative would be to define a new null-friendly type pattern. Let?s talk about it! > > Thanks, > Gavin > > > > On 14 Jul 2021, at 23:43, Manoj Palat wrote: > > Hi Gavin, All, > > Inhttp://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1, Section 14.11, I see "If a switch label has a null case label element then if the switch label also has any pattern case element labels, they must be type patterns (14.30.1)." implying that the following code: > > private static void foo(Object o) { > switch (o) { > case null, Integer i && i > 10 -> System.out.println(0); // flag error? > default -> System.out.println(o); > } > should have an error flagged for case label with null since the pattern case label element is not a type pattern but a guarded pattern. > Any reason for excluding guarded patterns here? Or does this requires a modification in spec to include guarded patterns as well? > > Regards, > Manoj > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jul 19 14:01:48 2021 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 19 Jul 2021 16:01:48 +0200 (CEST) Subject: [External] : Re: case null and type pattern In-Reply-To: <812efdc9-ba93-0271-f772-3ce24861e9de@oracle.com> References: <8E0D6D09-E4E6-4D7C-AC2D-CBE71727BD50@oracle.com> <66a6e6ec-bd3c-2e4c-a7c8-a7067a50fb0a@oracle.com> <2071220884.417222.1626700191999.JavaMail.zimbra@u-pem.fr> <812efdc9-ba93-0271-f772-3ce24861e9de@oracle.com> Message-ID: <794623100.446306.1626703308616.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" > Cc: "Gavin Bierman" , "Manoj Palat" > , "amber-spec-experts" > > Sent: Lundi 19 Juillet 2021 15:49:22 > Subject: Re: [External] : Re: case null and type pattern > Sure, but again, I think users would find that restriction even more annoying; > the vast majority of guards in languages with guarded patterns use the bindings > to refine the match. I'm not sure users would thank us for moving the > restriction in this way. I was thinking about the binding after the ':' or '->', not the fact that the same binding is used inside a guard, because it's not exactly the same binding. Let's compare case String s && s != null -> and case null, String s && s != null -> in the second case, there is a 'merge' between null and String s, there are two questions, do we allow this kind of merge and does a merge with null a special kind of merge. > A more interesting flavor of the same question, though, is what happens if we > allow _ to be used as a binding name. Then, the restriction on fall-through > could be relaxed if we wanted; it would be safe to do: > case Foo _, Bar _: blah > whereas today we don't allow mixing of these patterns. I agree, it seems we want to allow some merging (with null, with '_', are there others ??) but not the general form. R?mi > On 7/19/2021 9:09 AM, Remi Forax wrote: >>> From: "Brian Goetz" [ mailto:brian.goetz at oracle.com | ] >>> To: "Gavin Bierman" [ mailto:gavin.bierman at oracle.com | >>> ] , "Manoj Palat" [ mailto:manoj.palat at in.ibm.com | >>> ] >>> Cc: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net | >>> ] >>> Sent: Lundi 19 Juillet 2021 15:00:16 >>> Subject: Re: case null and type pattern >>> Right. In theory, we could allow guarded patterns here, but they will be hard to >>> use, since users will likely want to use the binding in the guard, and would >>> have to check for nullity every time. >> As you said, the error is to be able to use the binding not to be able to >> express that pattern. >> R?mi >>> On 7/19/2021 6:16 AM, Gavin Bierman wrote: >>>> Hi Manoj, >>>> This is certainly something we can discuss for Preview 2, but?it is intentional >>>> for now. The `case null, T t` is really a special case of extending a type >>>> pattern to be null friendly. Remember that the pattern variable `t` is >>>> initialised with null if this case label applies. >>>> The problem with what you are suggesting is that we?re going to end up with code >>>> like: >>>> switch(o) { >>>> ? >>>> case null, String s && s != null -> ? >>>> } >>>> Now the developer has a guard saying that `s` is not null, and is probably going >>>> to assume this in the body of the switch rule, but has erroneously extended the >>>> case label with null. >>>> So, it?s just an attempt to remove potential puzzlers by design. But we can take >>>> a look at whether we can capture a more generous definition. An alternative >>>> would be to define a new null-friendly type pattern. Let?s talk about it! >>>> Thanks, >>>> Gavin >>>>> On 14 Jul 2021, at 23:43, Manoj Palat [ mailto:manoj.palat at in.ibm.com | >>>>> ] wrote: >>>>> Hi Gavin, All, >>>>> In [ >>>>> http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1 >>>>> | >>>>> http://cr.openjdk.java.net/~gbierman/jep406/jep406-20210608/specs/patterns-switch-jls.html#jls-14.30.1 >>>>> ] , Section 14.11, I see "If a switch label has a null case label element then >>>>> if the switch label also has any pattern case element labels, they must be type >>>>> patterns (14.30.1)." implying that the following code: >>>>> private static void foo(Object o) { >>>>> switch (o) { >>>>> case null, Integer i && i > 10 -> System.out.println(0); // flag error? >>>>> default -> System.out.println(o); >>>>> } >>>>> should have an error flagged for case label with null since the pattern case >>>>> label element is not a type pattern but a guarded pattern. >>>>> Any reason for excluding guarded patterns here? Or does this requires a >>>>> modification in spec to include guarded patterns as well? >>>>> Regards, >>>>> Manoj -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jul 20 13:15:52 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 20 Jul 2021 09:15:52 -0400 Subject: Fwd: switch-case-catch In-Reply-To: References: Message-ID: <8c03c80b-9bdf-ea32-7198-e68d1a00d6c3@oracle.com> The following idea was received on amber-spec-comments. Essentially, the OP is asking that, if we're extending switch to cover more different kinds of cases, why not further extend it to treat an exception in evaluating the target as another kind of matchable result.? It is a little like the current treatment of null, where, if there is a `null` case, it is treated as a switchable value, and if not, the switch completes abruptly with NPE. While the questions are fair questions to ask, I see several challenges with this approach: ?- The motivations are mostly syntactic; `case X -> S` is more compact than `catch (E e) { S }`.? If these two had similar syntactic weight, it is less likely such suggestions would be made. ?- It only reduces the syntactic weight of try-catch in switches, but not other constructs.? This will surely be a "tomorrow's problems come from today's 'solutions'" thing. ?- People were uncomfortable about the "action at a distance" problem with other aspects of switch; this creates new ones (you have to scan all the cases to see which exceptions in evaluating the target are handled.) ?- The `catch` case labels are not being matched against the target of the switch, making the basic model for the body of a switch more complicated. ?- There are two possible interpretations of the approach; the desugaring suggested in the note seems the less sensible of the two to me.? They are: ?? - The switch target is evaluated.? If evaluation completes normally, the non-catch cases are tried, otherwise the switch completes abruptly.? If evaluation completes exceptionally, the catch cases are tried, and if none of those match, the switch completes abruptly.? In this version, only exceptions thrown by evaluation of the target can match. ?? - The entire switch is considered to be wrapped by a big try-catch (this is what the OP suggests.)? This means exceptions thrown from the body of a previous case can also be caught by a later catch case. If the motivation is to be more like a traditional Result, the former interpretation makes more sense, because it more directly models switching over a Result, with sugar to deconstruct the Result wrapper. -------- Forwarded Message -------- Subject: switch-case-catch Date: Tue, 20 Jul 2021 09:01:14 +0300 From: Omar Aloraini To: amber-spec-comments at openjdk.java.net Hello, With the introduction of sealed hierarchies and the enhancement of switch expressions, I find myself wondering about the way I model errors. Previously using a class `Result` to model success or failure of an operation was something that I used to do, but I quickly reverted as all my code base methods returned the `Result` type, sometime I introduce another type to model the error type such as Result, where E is typically a subtype of Exception. But again things get cumbersome, fortunately we now have sealed hierarchies, which allows us to model multiple possible outcomes of an operation, or a closed set of error conditions. Client code is not ugly anymore as switch expressions and pattern matching amend it to become more concise and pleasant to read and write, or as someone whom I respected would say ?less ceremony?. Now I find myself wondering why not use Exceptions? For some method A that returns a value of type *T* or throws an exception of two possible types, something like `T A() throws E1,E2`, obviously you will lose that pleasant client code with switch and pattern matching. But what if we had switch-case-catch where the catch clauses must cover all exceptions in the throws clause for it to compile. Something like the following: Object r = switch (o.A()){ case T t -> ... catch E1 e -> ... catch E1 e -> ... } Semantically it would equivalent to: Object r; try { r = switch (o.A()) { case T t -> ... } catch (E1 e) { r = ... } catch (E2 e) { r = ... } } You will also get the benefit of pattern matching for the catch clause, something like `catch E1 | E2`. I don't think it's necessary to use the `catch` keyword, `case` might just work. Apologies if this was suggested before. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jul 20 13:58:26 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 20 Jul 2021 09:58:26 -0400 Subject: Fwd: switch-case-catch In-Reply-To: <8c03c80b-9bdf-ea32-7198-e68d1a00d6c3@oracle.com> References: <8c03c80b-9bdf-ea32-7198-e68d1a00d6c3@oracle.com> Message-ID: <04daed50-8d81-ebf1-87cd-fe1ee452b5de@oracle.com> After a few minutes more thought, this approach can be simulated pretty well by a library solution. Suppose you wrote the following class (once): ??? sealed class Result { ??????? record Succ(T t) extends Result { } ??????? record Fail(E e) extends Result { } ??????? interface ExceptionalSupplier { ??????????? T get() throws E; ??????? } ??????? static Result of(ExceptionalSupplier s) { ??????????? try { ??????????????? return new Succ(s.get()); ??????????? } ??????????? catch (Exception e) { ??????????????? return new Fail(e); ??????????? } ? ? ? ? } ??? } Note that no library needs to directly return Result; you can wrap an exceptional computation with Result.of(() -> expr). Now, you can switch using ordinary pattern matching: ??? switch (Result.of(() -> possiblyExceptionalExpression()) { ??????? case Succ(T t) -> ... ??????? case Fail(E e) -> ... ??? } And note that nested patterns work just fine here: ??? switch (Result.of(() -> possiblyExceptionalExpression()) { ??????? case Succ(T t) -> ... ??????? case Fail(FooException e) -> ... ??????? case Fail(BarException e) -> ... } (Critics will say "but, that's more ceremony", because you have to wrap with a Result and then distinguish between Succ and Fail.? Supporters would say that this is a small price for saying what you mean.) On 7/20/2021 9:15 AM, Brian Goetz wrote: > The following idea was received on amber-spec-comments. > > Essentially, the OP is asking that, if we're extending switch to cover > more different kinds of cases, why not further extend it to treat an > exception in evaluating the target as another kind of matchable > result.? It is a little like the current treatment of null, where, if > there is a `null` case, it is treated as a switchable value, and if > not, the switch completes abruptly with NPE. > > While the questions are fair questions to ask, I see several > challenges with this approach: > > ?- The motivations are mostly syntactic; `case X -> S` is more compact > than `catch (E e) { S }`.? If these two had similar syntactic weight, > it is less likely such suggestions would be made. > > ?- It only reduces the syntactic weight of try-catch in switches, but > not other constructs.? This will surely be a "tomorrow's problems come > from today's 'solutions'" thing. > > ?- People were uncomfortable about the "action at a distance" problem > with other aspects of switch; this creates new ones (you have to scan > all the cases to see which exceptions in evaluating the target are > handled.) > > ?- The `catch` case labels are not being matched against the target of > the switch, making the basic model for the body of a switch more > complicated. > > ?- There are two possible interpretations of the approach; the > desugaring suggested in the note seems the less sensible of the two to > me.? They are: > > ?? - The switch target is evaluated.? If evaluation completes > normally, the non-catch cases are tried, otherwise the switch > completes abruptly.? If evaluation completes exceptionally, the catch > cases are tried, and if none of those match, the switch completes > abruptly.? In this version, only exceptions thrown by evaluation of > the target can match. > > ?? - The entire switch is considered to be wrapped by a big try-catch > (this is what the OP suggests.)? This means exceptions thrown from the > body of a previous case can also be caught by a later catch case. > > If the motivation is to be more like a traditional Result, the > former interpretation makes more sense, because it more directly > models switching over a Result, with sugar to deconstruct the > Result wrapper. > > > -------- Forwarded Message -------- > Subject: switch-case-catch > Date: Tue, 20 Jul 2021 09:01:14 +0300 > From: Omar Aloraini > To: amber-spec-comments at openjdk.java.net > > > > Hello, > > > With the introduction of sealed hierarchies and the enhancement of switch > expressions, I find myself wondering about the way I model errors. > Previously using a class `Result` to model success or failure of an > operation was something that I used to do, but I quickly reverted as > all my > code base methods returned the `Result` type, sometime I introduce another > type to model the error type such as Result, where E is typically a > subtype of Exception. But again things get cumbersome, fortunately we now > have sealed hierarchies, which allows us to model multiple possible > outcomes of an operation, or a closed set of error conditions. Client code > is not ugly anymore as switch expressions and pattern matching amend it to > become more concise and pleasant to read and write, or as someone whom I > respected would say ?less ceremony?. Now I find myself wondering why not > use Exceptions? For some method A that returns a value of type *T* or > throws an exception of two possible types, something like `T A() throws > E1,E2`, obviously you will lose that pleasant client code with switch and > pattern matching. But what if we had switch-case-catch where the catch > clauses must cover all exceptions in the throws clause for it to compile. > > Something like the following: > > Object r = switch (o.A()){ > > case T t -> ... > > catch E1 e -> ... > > catch E1 e -> ... > > } > > Semantically it would equivalent to: > > Object r; > > try { > > r = switch (o.A()) { > > case T t -> ... > > } catch (E1 e) { > > r = ... > > } catch (E2 e) { > > r = ... > > } > > } > > You will also get the benefit of pattern matching for the catch clause, > something like `catch E1 | E2`. > > I don't think it's necessary to use the `catch` keyword, `case` might just > work. > > Apologies if this was suggested before. -------------- next part -------------- An HTML attachment was scrubbed... URL: From daniel.smith at oracle.com Fri Jul 23 22:28:08 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 23 Jul 2021 22:28:08 +0000 Subject: Switch coverage with multiple branches Message-ID: <870F7B54-9A9E-414F-9A7B-D12DB33FB288@oracle.com> An RFE for JEP 406 (or maybe bug fix? I haven't dug into what the spec says). Can we make this compile? public class SwitchCoverage { sealed interface A {} sealed interface B1 extends A {} sealed interface B2 extends A {} sealed interface C extends A {} final class D1 implements B1, C {} final class D2 implements B2, C {} void test(A arg) { int i = switch (arg) { case B1 b1 -> 1; case B2 b2 -> 2; }; } } Output: % -> `javahome 17`/bin/javac --enable-preview --release 17 SwitchCoverage.java SwitchCoverage.java:10: error: the switch expression does not cover all possible input values int i = switch (arg) { ^ Note: SwitchCoverage.java uses preview features of Java SE 17. Note: Recompile with -Xlint:preview for details. 1 error The compiler wants to see a 'case C c', not realizing that the combination of B1 and B2 already covers C. The use case might seem a impractically complex, but I think it's actually pretty normal to want to define a universe of values (that's A), provide some implementations (D1 and D2), and then categorize the implementations in different dimensions (B1/B2 in one dimension, C in another). When I'm writing my switch, I might only care about one of these dimensions. From forax at univ-mlv.fr Fri Jul 23 22:48:27 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 24 Jul 2021 00:48:27 +0200 (CEST) Subject: Switch coverage with multiple branches In-Reply-To: <870F7B54-9A9E-414F-9A7B-D12DB33FB288@oracle.com> References: <870F7B54-9A9E-414F-9A7B-D12DB33FB288@oracle.com> Message-ID: <1000292465.767558.1627080507479.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "daniel smith" > To: "amber-spec-experts" > Cc: "Gavin Bierman" > Sent: Samedi 24 Juillet 2021 00:28:08 > Subject: Switch coverage with multiple branches > An RFE for JEP 406 (or maybe bug fix? I haven't dug into what the spec says). > Can we make this compile? > > public class SwitchCoverage { > sealed interface A {} > sealed interface B1 extends A {} > sealed interface B2 extends A {} > sealed interface C extends A {} > final class D1 implements B1, C {} > final class D2 implements B2, C {} > > void test(A arg) { > int i = switch (arg) { > case B1 b1 -> 1; > case B2 b2 -> 2; > }; > } > > } > > Output: > > % -> `javahome 17`/bin/javac --enable-preview --release 17 SwitchCoverage.java > SwitchCoverage.java:10: error: the switch expression does not cover all possible > input values > int i = switch (arg) { > ^ > Note: SwitchCoverage.java uses preview features of Java SE 17. > Note: Recompile with -Xlint:preview for details. > 1 error > > The compiler wants to see a 'case C c', not realizing that the combination of B1 > and B2 already covers C. > > The use case might seem a impractically complex, but I think it's actually > pretty normal to want to define a universe of values (that's A), provide some > implementations (D1 and D2), and then categorize the implementations in > different dimensions (B1/B2 in one dimension, C in another). When I'm writing > my switch, I might only care about one of these dimensions. Should it compile if C is declared as non-sealed ? R?mi From daniel.smith at oracle.com Sun Jul 25 17:02:09 2021 From: daniel.smith at oracle.com (Dan Smith) Date: Sun, 25 Jul 2021 17:02:09 +0000 Subject: [External] : Re: Switch coverage with multiple branches In-Reply-To: <1000292465.767558.1627080507479.JavaMail.zimbra@u-pem.fr> References: <870F7B54-9A9E-414F-9A7B-D12DB33FB288@oracle.com> <1000292465.767558.1627080507479.JavaMail.zimbra@u-pem.fr> Message-ID: <4DCA86F8-AF7B-4287-A678-B346CB7FF9E7@oracle.com> > On Jul 23, 2021, at 3:48 PM, Remi Forax wrote: > > ----- Original Message ----- >> From: "daniel smith" >> To: "amber-spec-experts" >> Cc: "Gavin Bierman" >> Sent: Samedi 24 Juillet 2021 00:28:08 >> Subject: Switch coverage with multiple branches > >> An RFE for JEP 406 (or maybe bug fix? I haven't dug into what the spec says). >> Can we make this compile? >> >> public class SwitchCoverage { >> sealed interface A {} >> sealed interface B1 extends A {} >> sealed interface B2 extends A {} >> sealed interface C extends A {} >> final class D1 implements B1, C {} >> final class D2 implements B2, C {} >> >> void test(A arg) { >> int i = switch (arg) { >> case B1 b1 -> 1; >> case B2 b2 -> 2; >> }; >> } >> >> } >> >> Output: >> >> % -> `javahome 17`/bin/javac --enable-preview --release 17 SwitchCoverage.java >> SwitchCoverage.java:10: error: the switch expression does not cover all possible >> input values >> int i = switch (arg) { >> ^ >> Note: SwitchCoverage.java uses preview features of Java SE 17. >> Note: Recompile with -Xlint:preview for details. >> 1 error >> >> The compiler wants to see a 'case C c', not realizing that the combination of B1 >> and B2 already covers C. >> >> The use case might seem a impractically complex, but I think it's actually >> pretty normal to want to define a universe of values (that's A), provide some >> implementations (D1 and D2), and then categorize the implementations in >> different dimensions (B1/B2 in one dimension, C in another). When I'm writing >> my switch, I might only care about one of these dimensions. > > Should it compile if C is declared as non-sealed ? No. The analysis would see that C = { D1, D2 } and then note that { D1, D2 } is covered by { B1, B2 } From brian.goetz at oracle.com Fri Jul 30 14:52:23 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 30 Jul 2021 10:52:23 -0400 Subject: Minor improvement to anonymous classes Message-ID: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> I have been working on a library where I've found myself repeatedly refactoring what should be anonymous classes into named (often local) classes, for the sole reason that I want to combine interfaces with an abstract base class: ??? interface Foo { ... lots of stuff .. } ??? abstract class AbstractFoo { ... lots of base implementation ... } ??? interface RedFoo extends Foo { void red(); } and I want a factory that yields a RedFoo that is based on AbstractFoo and implements red().? Trivial with a named class, but there's no reason I should not be able to do that with an anonymous class, since I have no need of the name. We already address this problem elsewhere; there are several places in the grammar where you can append additional _interfaces_ with &, such as: ??? class X { ... } and casts (which can be target types for lambdas.) These are not full-blown intersection types, but accomodate for the fact that classes have one superclass and potentially multiple interfaces.? It appears simple to extend this to inner class creation expressions: ??? new AbstractFoo(args) & RedFoo { ... } This would also smooth out a rough edge refactoring between lambdas and anonymous classes. I suspect there are one or two other places in the spec that could use this treatment. (Note that this is explicitly *not* a call for "let's do full-blown intersection types"; this is solely about class declaration.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Fri Jul 30 22:14:46 2021 From: john.r.rose at oracle.com (John Rose) Date: Fri, 30 Jul 2021 22:14:46 +0000 Subject: Minor improvement to anonymous classes In-Reply-To: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> Message-ID: <2ED211EA-3F96-4129-B5BF-9A262C917D9F@oracle.com> It seems reasonable: If you can do this for lambdas, which are (sort of) a concise anonymous inner class notation, you should be able to do this with the next level of notation, anonymous inner classes. It?s ungraceful to have MI on lambdas and named inner classes, but not the intermediate notation. Any similar limitations to consider loosening at this time? Are there things with generics you can do with lambdas and named ICs but not AICs? > On Jul 30, 2021, at 7:52 AM, Brian Goetz wrote: > > I have been working on a library where I've found myself repeatedly refactoring what should be anonymous classes into named (often local) classes, for the sole reason that I want to combine interfaces with an abstract base class: > > interface Foo { ... lots of stuff .. } > abstract class AbstractFoo { ... lots of base implementation ... } > > interface RedFoo extends Foo { void red(); } > > and I want a factory that yields a RedFoo that is based on AbstractFoo and implements red(). Trivial with a named class, but there's no reason I should not be able to do that with an anonymous class, since I have no need of the name. > > We already address this problem elsewhere; there are several places in the grammar where you can append additional _interfaces_ with &, such as: > > class X { ... } > > and casts (which can be target types for lambdas.) > > These are not full-blown intersection types, but accomodate for the fact that classes have one superclass and potentially multiple interfaces. It appears simple to extend this to inner class creation expressions: > > new AbstractFoo(args) & RedFoo { ... } > > This would also smooth out a rough edge refactoring between lambdas and anonymous classes. > > I suspect there are one or two other places in the spec that could use this treatment. > > (Note that this is explicitly *not* a call for "let's do full-blown intersection types"; this is solely about class declaration.) > > From amaembo at gmail.com Sat Jul 31 03:44:40 2021 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 31 Jul 2021 10:44:40 +0700 Subject: Minor improvement to anonymous classes In-Reply-To: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> Message-ID: I support this suggestion. I stumbled upon this problem many times. Kotlin allows such declarations [1] object : AbstractFoo(args), RedFoo {...} Seems there's no conceptual difficulties with this enhancement. With best regards, Tagir Valeev. [1] https://kotlinlang.org/spec/expressions.html#object-literals On Fri, Jul 30, 2021 at 9:53 PM Brian Goetz wrote: > > I have been working on a library where I've found myself repeatedly refactoring what should be anonymous classes into named (often local) classes, for the sole reason that I want to combine interfaces with an abstract base class: > > interface Foo { ... lots of stuff .. } > abstract class AbstractFoo { ... lots of base implementation ... } > > interface RedFoo extends Foo { void red(); } > > and I want a factory that yields a RedFoo that is based on AbstractFoo and implements red(). Trivial with a named class, but there's no reason I should not be able to do that with an anonymous class, since I have no need of the name. > > We already address this problem elsewhere; there are several places in the grammar where you can append additional _interfaces_ with &, such as: > > class X { ... } > > and casts (which can be target types for lambdas.) > > These are not full-blown intersection types, but accomodate for the fact that classes have one superclass and potentially multiple interfaces. It appears simple to extend this to inner class creation expressions: > > new AbstractFoo(args) & RedFoo { ... } > > This would also smooth out a rough edge refactoring between lambdas and anonymous classes. > > I suspect there are one or two other places in the spec that could use this treatment. > > (Note that this is explicitly *not* a call for "let's do full-blown intersection types"; this is solely about class declaration.) > > From amaembo at gmail.com Sat Jul 31 03:55:47 2021 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 31 Jul 2021 10:55:47 +0700 Subject: Minor improvement to anonymous classes In-Reply-To: References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> Message-ID: By the way, assuming that we implement this, new IFace() {...} would be technically equivalent to new Object(), IFace {...}. So this essentially means that specifying the Object superclass is optional "when interface is present". We can go a little bit further: 1. Allow new IFace {...}, without empty parentheses, as there's technically no constructor in the interface we are delegating to. Instead, we are delegating to the Object constructor, but Object is not mentioned explicitly, so these () always looked confusing to me. We will still allow new IFace() {...} for compatibility. 2. Allow new {...} instead of new Object() {...}. So the specification roughly should be like this: new , , ,... {...} = () could be omitted if it's java.lang.Object() (for compatibility) if there's exactly one interface and superclass_spec is omitted, it's allowed to put round parentheses after the interface name. With best regards, Tagir Valeev. On Sat, Jul 31, 2021 at 10:44 AM Tagir Valeev wrote: > > I support this suggestion. I stumbled upon this problem many times. > Kotlin allows such declarations [1] > object : AbstractFoo(args), RedFoo {...} > Seems there's no conceptual difficulties with this enhancement. > > With best regards, > Tagir Valeev. > > [1] https://kotlinlang.org/spec/expressions.html#object-literals > > On Fri, Jul 30, 2021 at 9:53 PM Brian Goetz wrote: > > > > I have been working on a library where I've found myself repeatedly refactoring what should be anonymous classes into named (often local) classes, for the sole reason that I want to combine interfaces with an abstract base class: > > > > interface Foo { ... lots of stuff .. } > > abstract class AbstractFoo { ... lots of base implementation ... } > > > > interface RedFoo extends Foo { void red(); } > > > > and I want a factory that yields a RedFoo that is based on AbstractFoo and implements red(). Trivial with a named class, but there's no reason I should not be able to do that with an anonymous class, since I have no need of the name. > > > > We already address this problem elsewhere; there are several places in the grammar where you can append additional _interfaces_ with &, such as: > > > > class X { ... } > > > > and casts (which can be target types for lambdas.) > > > > These are not full-blown intersection types, but accomodate for the fact that classes have one superclass and potentially multiple interfaces. It appears simple to extend this to inner class creation expressions: > > > > new AbstractFoo(args) & RedFoo { ... } > > > > This would also smooth out a rough edge refactoring between lambdas and anonymous classes. > > > > I suspect there are one or two other places in the spec that could use this treatment. > > > > (Note that this is explicitly *not* a call for "let's do full-blown intersection types"; this is solely about class declaration.) > > > > From john.r.rose at oracle.com Sat Jul 31 18:00:20 2021 From: john.r.rose at oracle.com (John Rose) Date: Sat, 31 Jul 2021 18:00:20 +0000 Subject: Minor improvement to anonymous classes In-Reply-To: References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> Message-ID: On Jul 30, 2021, at 8:55 PM, Tagir Valeev > wrote: 2. Allow new {...} instead of new Object() {?}. I would think *that* would make a very useful poly expression. Building ?new Object() {?}? has far fewer use cases than ?build me an object of the type the context requires, with these methods?. My overall reaction to the extra syntax tweaks to the existing AIC syntax is, ?meh?. The syntax is inherently ceremonious; why trouble to elide an extra token at the head? -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Jul 31 18:24:47 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 31 Jul 2021 20:24:47 +0200 (CEST) Subject: Minor improvement to anonymous classes In-Reply-To: <2ED211EA-3F96-4129-B5BF-9A262C917D9F@oracle.com> References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> <2ED211EA-3F96-4129-B5BF-9A262C917D9F@oracle.com> Message-ID: <1320762308.901257.1627755887681.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "John Rose" > To: "Brian Goetz" > Cc: "amber-spec-experts" > Sent: Samedi 31 Juillet 2021 00:14:46 > Subject: Re: Minor improvement to anonymous classes > It seems reasonable: If you can do this for lambdas, > which are (sort of) a concise anonymous inner class > notation, you should be able to do this with the next > level of notation, anonymous inner classes. It?s > ungraceful to have MI on lambdas and named inner > classes, but not the intermediate notation. > > Any similar limitations to consider loosening at this time? > Are there things with generics you can do with lambdas > and named ICs but not AICs? There is a similar limitation, anonymous classes and local classes always capture the enclosing "this" even if they do not use it, so when you use an anonymous class, the lifetime of the enclosing this is extended for no good reason. The usual issue it to have a code that relies on the finalizer to close a system resource (which is not a good idea) that fails to close the resource because the is captured by the anonymous class. Lambdas do not have this weird behavior. We have discussed about allowing static in front of a local class but there is no way to ask for a static anonymous class. regards, R?mi > >> On Jul 30, 2021, at 7:52 AM, Brian Goetz wrote: >> >> I have been working on a library where I've found myself repeatedly refactoring >> what should be anonymous classes into named (often local) classes, for the sole >> reason that I want to combine interfaces with an abstract base class: >> >> interface Foo { ... lots of stuff .. } >> abstract class AbstractFoo { ... lots of base implementation ... } >> >> interface RedFoo extends Foo { void red(); } >> >> and I want a factory that yields a RedFoo that is based on AbstractFoo and >> implements red(). Trivial with a named class, but there's no reason I should >> not be able to do that with an anonymous class, since I have no need of the >> name. >> >> We already address this problem elsewhere; there are several places in the >> grammar where you can append additional _interfaces_ with &, such as: >> >> class X { ... } >> >> and casts (which can be target types for lambdas.) >> >> These are not full-blown intersection types, but accomodate for the fact that >> classes have one superclass and potentially multiple interfaces. It appears >> simple to extend this to inner class creation expressions: >> >> new AbstractFoo(args) & RedFoo { ... } >> >> This would also smooth out a rough edge refactoring between lambdas and >> anonymous classes. >> >> I suspect there are one or two other places in the spec that could use this >> treatment. >> >> (Note that this is explicitly *not* a call for "let's do full-blown intersection >> types"; this is solely about class declaration.) >> From forax at univ-mlv.fr Sat Jul 31 18:29:05 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 31 Jul 2021 20:29:05 +0200 (CEST) Subject: Minor improvement to anonymous classes In-Reply-To: References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> Message-ID: <1262238076.901726.1627756145331.JavaMail.zimbra@u-pem.fr> > From: "John Rose" > To: "Tagir Valeev" > Cc: "Brian Goetz" , "amber-spec-experts" > > Sent: Samedi 31 Juillet 2021 20:00:20 > Subject: Re: Minor improvement to anonymous classes > On Jul 30, 2021, at 8:55 PM, Tagir Valeev < [ mailto:amaembo at gmail.com | > amaembo at gmail.com ] > wrote: >> 2. Allow new {...} instead of new Object() {?}. > I would think *that* would make a very useful poly expression. > Building ?new Object() {?}? has far fewer use cases than > ?build me an object of the type the context requires, with > these methods?. > My overall reaction to the extra syntax tweaks to the > existing AIC syntax is, ?meh?. The syntax is inherently > ceremonious; why trouble to elide an extra token at > the head? and i would prefer to keep that syntax available if at some point in the future we decide to allow classes (or perhaps just record) to be initialized in a literal way. record Person(String name, int age) {} var person = new Person { name = "Ana", age = 31 }; Person person = new { name = "Ana", age = 31 }; which is more readable once you starts to have more than 3 or 4 components (and avoid builders). R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Jul 31 18:32:37 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 31 Jul 2021 20:32:37 +0200 (CEST) Subject: Minor improvement to anonymous classes In-Reply-To: References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> Message-ID: <256185873.902064.1627756357040.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Tagir Valeev" > To: "Brian Goetz" > Cc: "amber-spec-experts" > Sent: Samedi 31 Juillet 2021 05:55:47 > Subject: Re: Minor improvement to anonymous classes > By the way, assuming that we implement this, new IFace() {...} would > be technically equivalent to new Object(), IFace {...}. So this > essentially means that specifying the Object superclass is optional > "when interface is present". We can go a little bit further: > > 1. Allow new IFace {...}, without empty parentheses, as there's > technically no constructor in the interface we are delegating to. > Instead, we are delegating to the Object constructor, but Object is > not mentioned explicitly, so these () always looked confusing to me. > We will still allow new IFace() {...} for compatibility. It's not only confusing for you, it's confusing for my students too, how the compiler is able to call the constructor of an interface is a question i get each year. > > With best regards, > Tagir Valeev. R?mi > > On Sat, Jul 31, 2021 at 10:44 AM Tagir Valeev wrote: >> >> I support this suggestion. I stumbled upon this problem many times. >> Kotlin allows such declarations [1] >> object : AbstractFoo(args), RedFoo {...} >> Seems there's no conceptual difficulties with this enhancement. >> >> With best regards, >> Tagir Valeev. >> >> [1] https://kotlinlang.org/spec/expressions.html#object-literals >> >> On Fri, Jul 30, 2021 at 9:53 PM Brian Goetz wrote: >> > >> > I have been working on a library where I've found myself repeatedly refactoring >> > what should be anonymous classes into named (often local) classes, for the sole >> > reason that I want to combine interfaces with an abstract base class: >> > >> > interface Foo { ... lots of stuff .. } >> > abstract class AbstractFoo { ... lots of base implementation ... } >> > >> > interface RedFoo extends Foo { void red(); } >> > >> > and I want a factory that yields a RedFoo that is based on AbstractFoo and >> > implements red(). Trivial with a named class, but there's no reason I should >> > not be able to do that with an anonymous class, since I have no need of the >> > name. >> > >> > We already address this problem elsewhere; there are several places in the >> > grammar where you can append additional _interfaces_ with &, such as: >> > >> > class X { ... } >> > >> > and casts (which can be target types for lambdas.) >> > >> > These are not full-blown intersection types, but accomodate for the fact that >> > classes have one superclass and potentially multiple interfaces. It appears >> > simple to extend this to inner class creation expressions: >> > >> > new AbstractFoo(args) & RedFoo { ... } >> > >> > This would also smooth out a rough edge refactoring between lambdas and >> > anonymous classes. >> > >> > I suspect there are one or two other places in the spec that could use this >> > treatment. >> > >> > (Note that this is explicitly *not* a call for "let's do full-blown intersection >> > types"; this is solely about class declaration.) >> > From forax at univ-mlv.fr Sat Jul 31 18:45:49 2021 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 31 Jul 2021 20:45:49 +0200 (CEST) Subject: Minor improvement to anonymous classes In-Reply-To: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> Message-ID: <2031341240.910579.1627757149926.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "amber-spec-experts" > Sent: Vendredi 30 Juillet 2021 16:52:23 > Subject: Minor improvement to anonymous classes > I have been working on a library where I've found myself repeatedly refactoring > what should be anonymous classes into named (often local) classes, for the sole > reason that I want to combine interfaces with an abstract base class: > interface Foo { ... lots of stuff .. } > abstract class AbstractFoo { ... lots of base implementation ... } > interface RedFoo extends Foo { void red(); } > and I want a factory that yields a RedFoo that is based on AbstractFoo and > implements red(). Trivial with a named class, but there's no reason I should > not be able to do that with an anonymous class, since I have no need of the > name. > We already address this problem elsewhere; there are several places in the > grammar where you can append additional _interfaces_ with &, such as: > class X { ... } > and casts (which can be target types for lambdas.) > These are not full-blown intersection types, but accommodate for the fact that > classes have one superclass and potentially multiple interfaces. It appears > simple to extend this to inner class creation expressions: > new AbstractFoo(args) & RedFoo { ... } > This would also smooth out a rough edge refactoring between lambdas and > anonymous classes. > I suspect there are one or two other places in the spec that could use this > treatment. > (Note that this is explicitly *not* a call for "let's do full-blown intersection > types"; this is solely about class declaration.) About the proposed syntax, i'm not sure using '&' is a good idea, it may clash with operators overloading if we allow them on primitive classes in the future. (i believe the compiler may be able to differentiate between the two because of the curly braces later but for a human it will be hard to make the difference without precisely reading the code). Philosophically, I think we should not add more features to inheritance, inheritance is the goto of OOP, yes there are some corner cases where it's kind a useful but at the same time you are pushing more people to discovering that inheritance trees are a mess to maintain because the behavior is spitted to several places and hard to refactor too. new RedFoo() { + delegation } leads to a code easier to understand and easier to maintain. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From cushon at google.com Sat Jul 31 19:43:36 2021 From: cushon at google.com (Liam Miller-Cushon) Date: Sat, 31 Jul 2021 12:43:36 -0700 Subject: Minor improvement to anonymous classes In-Reply-To: <1320762308.901257.1627755887681.JavaMail.zimbra@u-pem.fr> References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> <2ED211EA-3F96-4129-B5BF-9A262C917D9F@oracle.com> <1320762308.901257.1627755887681.JavaMail.zimbra@u-pem.fr> Message-ID: On Sat, Jul 31, 2021 at 11:25 AM Remi Forax wrote: > > Any similar limitations to consider loosening at this time? > > Are there things with generics you can do with lambdas > > and named ICs but not AICs? > > There is a similar limitation, anonymous classes and local classes always > capture the enclosing "this" even if they do not use it, > so when you use an anonymous class, the lifetime of the enclosing this is > extended for no good reason. > The usual issue it to have a code that relies on the finalizer to close a > system resource (which is not a good idea) that fails to close the resource > because the is captured by the anonymous class. Lambdas do not have this > weird behavior. > > We have discussed about allowing static in front of a local class but > there is no way to ask for a static anonymous class. > I think it'd be nice to have a way to get explicitly static anonymous classes, but I haven't weighed the tradeoffs very carefully. Another option that gets some of the benefit and doesn't require a spec change is to omit the reference to the enclosing instance if it is unused. We've been doing that successfully for a few years at Google. There was some minor compatibility impact to code using reflection to access the synthetic field that stores the reference to the enclosing instance, but that was very rare, and the benefit is that it avoids some memory leaks. I found a discussion in JDK-6324535 that touched on this, and mentioned the various options (static anonymous classes, optimizing away the enclosing instance reference). -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Jul 31 19:46:22 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 31 Jul 2021 15:46:22 -0400 Subject: [External] : Re: Minor improvement to anonymous classes In-Reply-To: <1320762308.901257.1627755887681.JavaMail.zimbra@u-pem.fr> References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> <2ED211EA-3F96-4129-B5BF-9A262C917D9F@oracle.com> <1320762308.901257.1627755887681.JavaMail.zimbra@u-pem.fr> Message-ID: <24af60b4-e2c5-1196-2c62-ff46f2cd9f95@oracle.com> > so when you use an anonymous class, the lifetime of the enclosing this is extended for no good reason. > The usual issue it to have a code that relies on the finalizer to close a system resource (which is not a good idea) that fails to close the resource because the is captured by the anonymous class. Lambdas do not have this weird behavior. > > We have discussed about allowing static in front of a local class but there is no way to ask for a static anonymous class. We've already laid the groundwork for that; the recent changes to allow for local interfaces/records/enums included some spec wording for static local members, even though you can't say `static` in all these places.? I'd like to address this as part of a more comprehensive scoping-alignment; the inner-class thing seems mostly a separate (and small) feature. > and i would prefer to keep that syntax available if at some point in > the future we decide to allow classes (or perhaps just record) to be > initialized in a literal way. Agree, I think the "return on syntax" here is limited. From brian.goetz at oracle.com Sat Jul 31 19:51:15 2021 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 31 Jul 2021 15:51:15 -0400 Subject: [External] : Re: Minor improvement to anonymous classes In-Reply-To: References: <424ad976-6f0c-5ada-ca22-f5a3d9c76dc1@oracle.com> <2ED211EA-3F96-4129-B5BF-9A262C917D9F@oracle.com> <1320762308.901257.1627755887681.JavaMail.zimbra@u-pem.fr> Message-ID: <32bade08-4697-a2fd-52d2-491822c14d19@oracle.com> > Another option that gets some of the benefit and doesn't require a > spec change is to omit the reference to the enclosing instance if it > is unused. We've been doing that successfully for a few years at > Google. There was some minor compatibility impact to code using > reflection to access the synthetic field that stores the reference to > the enclosing instance, but that was very rare, and the benefit is > that it avoids some memory leaks. The people who would complain are in a pretty weak position: ?- The field is private and synthetic; you shouldn't be reflecting on it at all; ?- The field name is an implementation detail of the compiler, and not specified, so you shouldn't even pretend you know its name; ?- If its your class, the easy thing to do is add an accessor, so the only people who would complain are those that are breaking into classes they don't control. I think it is reasonable for the language to infer whether the enclosing instance is "captured" by the inner class or not, and that's consistent with how lambdas behave.