From hjohn at xs4all.nl Tue Nov 1 08:04:39 2022 From: hjohn at xs4all.nl (John Hendrikx) Date: Tue, 01 Nov 2022 08:04:39 +0000 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> Message-ID: I've been looking at the examples, and I find none of them compelling. It seems to be primarily fighting against the symptoms of another problem: `this` references escaping. The `FilteredSet` example is a good example of why calling an instance method before an object is fully constructed is bad. In this case `HashSet` itself is calling a non-final method `addAll`, instead of a final `internalAddAll` or something similar. Furthermore, `FilteredSet` is relying on this undocumented behavior, which is bad as this presumably can still change in a future JDK. Furthermore, even with the current restrictions in place, a `FilteredSet` can be implemented fairly easily, so in itself that's not a good enough reason to relax the super calling requirements. The `BigPositiveValue` example is quite contrived (as you said), and not something I'd ever need to write. First, it perhaps feels more natural to check failing conditions before calling super, but as long as construction is prevented, the end result is the same. A smart compiler can re-order these I suppose. Second, giving the constructors radically different behaviors when numeric input is either `long` or `float` would never pass review. Static factory methods that can be named ("of(long)", "pow2(float)") would be ideally suited to disambiguate these and avoid potential bugs and confusion. I find the idea of having better warnings (or even errors) for `this` references escaping much more compelling. It should flag any method calls that aren't private or final, with implicit or explicit this references. This would highlight the mistakes made in `HashSet` allowing them to be fixed. --John ------ Original Message ------ >From "Archie Cobbs" To amber-dev at openjdk.java.net Cc "Vicente Romero" ; "Brian Goetz" Date 31/10/2022 20:18:19 Subject Re: Loosening requirements for super() invocation >This is an old thread which I've been looking at again lately. > >To summarize, the idea is to relax the JLS rules about code prior to >super()/this() invocation to more closely match what the JVM allows, >which is field assignments and any code that doesn't reference 'this'. >Relevant issues are JDK-8194743 > and JDK-8193760 >. > >In order to make this idea less theoretical, I've prototyped all the >required compiler changes. There is also a write-up describing them >. > >There are a few interesting subtleties, e.g. relating to initialization >blocks, and there is also a sub-question, which is whether to allow >invoking super()/this() within a try block (this would have to be under >the restriction that any catch or finally clause must not return >normally). Currently that's not possible without a small JVM change, >which I also think might be worth considering to really round out this >new feature. See the writeup for details. > >To see some examples of what would be allowed, see this unit test >. >The compiler changes are here >. > >Thoughts? > >-Archie > >On Thu, Jan 17, 2019 at 8:42 AM Brian Goetz >wrote: >>Some things have improved for this feature since we last talked; >>several verifier issues that this would have pushed on have been >>resolved. So it?s moved from the ?way too expensive for the benefit? >>category into the ?there are lots of things we can do, is this really >>what we want to spend our effort and complexity budget on? category. >> >>My view on this is that while there?s nothing wrong with it, it?s also >>a pretty minor wart. If this fell out of a bigger feature, I?d >>certainly not object, but I?d rather spend the effort and complexity >>budget on things that have broader benefit. >> >> > On Jan 16, 2019, at 5:48 PM, Archie Cobbs >>wrote: >> > >> > I'm curious what are people's current thoughts on loosening the >> > requirements for super() invocation in the context of Amber, e.g.: >> > >> > public class MyInputStream extends FilterInputStream { >> > public MyInputStream(InputStream in) { >> > if (in == null) >> > throw new IllegalArgumentException("null input"); >> > super(in); // look ma! >> > } >> > } >> > > >-- >Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Nov 1 15:43:45 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 1 Nov 2022 10:43:45 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> Message-ID: Thanks for the various comments. I agree the topic of "this" escaping is definitely an interesting one. Although there is a lot of code out there that currently relies on it (e.g., HashSet and AbstractCollection), perhaps someday we could realize this in a backward-compatible way by adding a notion of a "safe constructor" which would be limited to following more strict rules that disallowed "this" escapes. Perhaps you could declare your constructor (and/or initialized fields) to be "safe" with an annotation, e.g.: @SafeConstruction public MyClass(int x) { this.x = x; // ok this.someMethod(); // error here! } However I'd also like to keep this discussion organized with an important observation... The topic of "this" escape is completely orthogonal to this change. None of the code that would be allowed before super() or this() could allow "this" to escape, because you're not allowed to reference "this" in any way except for assigning values to fields. The only thing you can do is what might be called "static housekeeping". So this change does not make the problem of "this" escape any worse than it already is. That means the two problems are really separate and can be addressed separately and independently. So I agree that Java has issues with "this" escape, and there are some interesting ways we could address it, but I also don't see that as a reason to prevent people from being able to do "static housekeeping" prior to their this() or super() invocation. I think the "try" part is likely to be off the table; too intrusive for > too little value. > There is one other reason to consider the JVM change, which is that currently it's not possible for bytecode rewriting tools (e.g., aspectj) to fully instrument constructors. The StackOverlow link talks about this use case. Another minor factor is that the class file format wouldn't actually need to change, only how it's verified. Have you looked at the spec to see what would need to be adjusted? > I've not gotten that far yet but would be happy to help/participate with some guidance. Thanks, -Archie On Mon, Oct 31, 2022 at 5:08 PM Brian Goetz wrote: > This looks like some nice progress. > > I would really like it if we had some warnings to detect more of the cases > where `this` might escape construction, such as passing `this` explicitly > to a method, invoking a non-final instance method, or invoking an instance > method whose implementation is in a superclass. (Same for use of `this` in > init blocks.) > > I think the "try" part is likely to be off the table; too intrusive for > too little value. > > Have you looked at the spec to see what would need to be adjusted? > > > > > On 10/31/2022 3:18 PM, Archie Cobbs wrote: > > This is an old thread which I've been looking at again lately. > > To summarize, the idea is to relax the JLS rules about code prior to > super()/this() invocation to more closely match what the JVM allows, which > is field assignments and any code that doesn't reference 'this'. Relevant > issues are JDK-8194743 and > JDK-8193760 . > > In order to make this idea less theoretical, I've prototyped all the > required compiler changes. There is also a write-up describing them > > . > > There are a few interesting subtleties, e.g. relating to initialization > blocks, and there is also a sub-question, which is whether to allow > invoking super()/this() within a try block (this would have to be under the > restriction that any catch or finally clause must not return normally). > Currently that's not possible without a small JVM change, which I also > think might be worth considering to really round out this new feature. See > the writeup for details. > > To see some examples of what would be allowed, see this unit test > . > The compiler changes are here > > . > > Thoughts? > > -Archie > > On Thu, Jan 17, 2019 at 8:42 AM Brian Goetz > wrote: > >> Some things have improved for this feature since we last talked; several >> verifier issues that this would have pushed on have been resolved. So it?s >> moved from the ?way too expensive for the benefit? category into the ?there >> are lots of things we can do, is this really what we want to spend our >> effort and complexity budget on? category. >> >> My view on this is that while there?s nothing wrong with it, it?s also a >> pretty minor wart. If this fell out of a bigger feature, I?d certainly not >> object, but I?d rather spend the effort and complexity budget on things >> that have broader benefit. >> >> > On Jan 16, 2019, at 5:48 PM, Archie Cobbs >> wrote: >> > >> > I'm curious what are people's current thoughts on loosening the >> > requirements for super() invocation in the context of Amber, e.g.: >> > >> > public class MyInputStream extends FilterInputStream { >> > public MyInputStream(InputStream in) { >> > if (in == null) >> > throw new IllegalArgumentException("null input"); >> > super(in); // look ma! >> > } >> > } >> > >> > > -- > Archie L. Cobbs > > > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 1 16:45:10 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 1 Nov 2022 12:45:10 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> Message-ID: <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> > I agree the topic of "this" escaping is definitely an interesting one. > Although there is a lot of code out there that currently relies on it > (e.g., HashSet and AbstractCollection), perhaps someday we could > realize this in a backward-compatible way by adding a notion of a > "safe constructor" which would be limited to following more strict > rules that disallowed "this" escapes. So, what you're saying is that we can't immediately promote these to errors, but that doesn't mean we can't have warnings.? (We are fairly disciplined about zero-warnings in the JDK, using @SuppressWarnings to augment type checking with human judgment.) Warnings are an important part of the safety story.? For example, generics are subject to heap pollution because of erasure, but we make a strong guarantee anyway: if the entire codebase compiles without raw or unchecked warnings, then the synthetic casts introduced by the compiler at the boundary of generic and non-generic code are guaranteed to succeed.? Similarly, while it is a high bar, if we can reliably warn about this-escape, we can make a similar guarantee for the final-field initialization safety guarantees, which are voided by this-escape. > So this change does not make the problem of "this" escape any worse > than it already is. That means the two problems are really separate > and can be addressed separately and independently. I get where you're coming from; you have a patch that you'd like to see integrated, so you're trying to keep the requirements narrow. But realistically, this patch surely needs a JEP, because it affects the language spec and the user-visible programming model.? And such a JEP is going to be more compelling if it addresses the closely-related issue of this-escape. -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Nov 1 16:54:28 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 1 Nov 2022 17:54:28 +0100 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> Message-ID: Hello! As an IDE developer, I support allowing statements before this/super invocation, without referencing `this`. This is often an annoying edge case in many refactorings or quick-fixes. E.g.: - If you want to introduce local variable from an argument expression in this/super call, you cannot do this - If you want to inline method called inside this/super call (like inline method() in super(method(x))), you cannot do this, unless method() is a one-liner. Even with one-liner it's complicated, as we usually add variable declarations to assign parameters, then try to inline these declarations, but this approach doesn't work well here, as these temporary declarations are forbidden. - If you have a stream API call inside this/super call (like super(list.stream().map(...).filter(...).toList())), you cannot desugar it to loop, as there's no place to put this loop. On the other hand, I don't think that allowing `this` references (e.g., field initialization) before this/super call is a good idea. This would add more complexity to the specification and more confusion with little advantage. Supporting super() inside try also sounds like an overkill. Btw what about inside the synchronized {} section? With best regards, Tagir Valeev. On Mon, Oct 31, 2022 at 8:18 PM Archie Cobbs wrote: > > This is an old thread which I've been looking at again lately. > > To summarize, the idea is to relax the JLS rules about code prior to super()/this() invocation to more closely match what the JVM allows, which is field assignments and any code that doesn't reference 'this'. Relevant issues are JDK-8194743 and JDK-8193760. > > In order to make this idea less theoretical, I've prototyped all the required compiler changes. There is also a write-up describing them. > > There are a few interesting subtleties, e.g. relating to initialization blocks, and there is also a sub-question, which is whether to allow invoking super()/this() within a try block (this would have to be under the restriction that any catch or finally clause must not return normally). Currently that's not possible without a small JVM change, which I also think might be worth considering to really round out this new feature. See the writeup for details. > > To see some examples of what would be allowed, see this unit test. The compiler changes are here. > > Thoughts? > > -Archie > > On Thu, Jan 17, 2019 at 8:42 AM Brian Goetz wrote: >> >> Some things have improved for this feature since we last talked; several verifier issues that this would have pushed on have been resolved. So it?s moved from the ?way too expensive for the benefit? category into the ?there are lots of things we can do, is this really what we want to spend our effort and complexity budget on? category. >> >> My view on this is that while there?s nothing wrong with it, it?s also a pretty minor wart. If this fell out of a bigger feature, I?d certainly not object, but I?d rather spend the effort and complexity budget on things that have broader benefit. >> >> > On Jan 16, 2019, at 5:48 PM, Archie Cobbs wrote: >> > >> > I'm curious what are people's current thoughts on loosening the >> > requirements for super() invocation in the context of Amber, e.g.: >> > >> > public class MyInputStream extends FilterInputStream { >> > public MyInputStream(InputStream in) { >> > if (in == null) >> > throw new IllegalArgumentException("null input"); >> > super(in); // look ma! >> > } >> > } >> > > > > -- > Archie L. Cobbs From archie.cobbs at gmail.com Tue Nov 1 21:13:06 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 1 Nov 2022 16:13:06 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> Message-ID: On Tue, Nov 1, 2022 at 11:45 AM Brian Goetz wrote: > So, what you're saying is that we can't immediately promote these to > errors, but that doesn't mean we can't have warnings. (We are fairly > disciplined about zero-warnings in the JDK, using @SuppressWarnings to > augment type checking with human judgment.) > > Warnings are an important part of the safety story. For example, generics > are subject to heap pollution because of erasure, but we make a strong > guarantee anyway: if the entire codebase compiles without raw or unchecked > warnings, then the synthetic casts introduced by the compiler at the > boundary of generic and non-generic code are guaranteed to succeed. > Similarly, while it is a high bar, if we can reliably warn about > this-escape, we can make a similar guarantee for the final-field > initialization safety guarantees, which are voided by this-escape. > Yes, I really like this idea... so you could compile with something like javac -Xlint:thisEscape and get all the 'this' escapes reported as warnings. Developers could control it using the usual methods, i.e., command line flags and/or @SuppressWarnings annotations. Of course the devil is in the details... First of all, when should this rule apply? At any point in a constructor (one that successfully compiles), the new object is always definitively in one of three states: State 0: neither super() nor this() has been invoked; neither the superclass nor this class are initialized State 1: super() has been invoked, so the superclass is initialized, but at least one final field in this class has not been initialized State 2: super() has been invoked and all final fields in this class have been initialized BTW, the current state is easily tracked within the existing DA/DU analysis of the prototype patch. The possible state transitions are: - super() takes you from: - State 0 ? State 1 if there remain any uninitialized final fields - State 0 ? State 2 if there are no remaining uninitialized final fields - this() takes you from State 0 ? State 2 - Initializing the last final field takes you from: - State 0 ? State 0 - State 1 ? State 2 Presumably the 'this' escape analysis should only apply in State 1. This is when the object is vulnerable because it's half initialized. One might also argue that "uninitialized final fields" should be replaced by "fields that are yet to be assigned later in the constructor" or somesuch. After all, just because a field isn't final, that doesn't mean it's not requiring initialization. Or maybe there should be some other way to denote such fields, e.g., a new @RequiresInit annotation. Assuming you're in State 1, what exactly would constitute a detectable 'this' escape? Could be something like this: - Invoking any non-static method on an object known to be equal to 'this'. - Passing an object known to be equal to 'this' as a parameter to any method or constructor (including implicit outer instance and free variable proxy constructor parameters). - Throwing 'this' In other words, the 'this' instance literally cannot escape the constructor's stack frame. For the meaning of "known to be equal to 'this'", we would be limited in our ability to infer it in the usual ways (Turing complete, blah blah), so you'd still be able to evade the warning e.g.: public class MyClass implements Iterator { private final int limit; private int count; public MyClass(int limit) throws IOException { ((Iterator)(Object)this).hasNext(); // always returns false! this.limit = limit; } @Override public boolean hasNext() { return this.count < this.limit; } } So it doesn't seem like an ironclad guarantee like "no 'this' escape warnings implies no uninitialized final variables" analogous to the "no unchecked warnings implies no ClassCastExceptions" is possible. But still, knowing your constructor generates zero warnings would be really useful. So what do you think would be reasonable definitions for "detectable 'this' escape" and "known to be equal to 'this'" ? > > So this change does not make the problem of "this" escape any worse than > it already is. That means the two problems are really separate and can be > addressed separately and independently. > > > I get where you're coming from; you have a patch that you'd like to see > integrated, so you're trying to keep the requirements narrow. But > realistically, this patch surely needs a JEP, because it affects the > language spec and the user-visible programming model. And such a JEP is > going to be more compelling if it addresses the closely-related issue of > this-escape. > That sounds fine to me as long as we can satisfy ourselves with the requisite details. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 1 21:30:24 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 1 Nov 2022 17:30:24 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> Message-ID: > First of all, when should this rule apply? > > At any point in a constructor (one that successfully compiles), the > new object is always definitively in one of three states: > > State 0: neither super() nor this() has been invoked; neither the > superclass nor this class are initialized > State 1: super() has been invoked, so the superclass is initialized, > but at least one final field in this class has not been initialized > State 2: super() has been invoked and all final fields in this class > have been initialized > > BTW, the current state is easily tracked within the existing DA/DU > analysis of the prototype patch. > > The possible state transitions are: > > - super() takes you from: > ? - State 0 ? State 1 if there remain any uninitialized final fields > ? - State 0 ? State 2 if there are no remaining uninitialized final fields > - this() takes you from State 0 ? State 2 > - Initializing the last final field takes you from: > ? - State 0 ? State 0 > ? - State 1 ? State 2 > > Presumably the 'this' escape analysis should only apply in State 1. > This is when the object is vulnerable because it's half initialized. In actuality, `this` can escape in any of the states; escaping means that `this` is exposed before the return from the constructor. (Even the very last line of the constructor is suspect.? A class could have subclasses, which means we're still in "the middle" of the subclass constructor, and even for a final class, the write barrier does not get emitted until after the constructor completes.) All of this said, we should probably tune warnings to the various states, since escape during State 0 is "even worse" than escape during State 2.? And we should add: ??? State 3: we're still in the constructor In any case, let's start with the strictest view and work backwards to relaxation.? The receiver may escape if: ?- a non-final instance method is invoked (*) ?- an instance method declared in a superclass is invoked ?- the variable `this` is used for anything other than field access We should analyze not only constructor code, but instance initializer blocks and instance field initializers as well.? We can exclude final instance methods declared in the same class if the same analysis of the method (recursively) shows no escape of `this` (this is the * in the first bullet above.) Inner class creation is a similar story as calling final methods in the same class; we can do a similar analysis of the inner class constructor to verify non-escape. > For the meaning of "known to be equal to 'this'", we would be limited > in our ability to infer it in the usual ways (Turing complete, blah > blah), so you'd still be able to evade the warning e.g.: For this to happen, `this` must have already escaped or have been assigned to another variable.? The above already detects this. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Nov 1 22:46:23 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 1 Nov 2022 17:46:23 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> Message-ID: On Tue, Nov 1, 2022 at 4:30 PM Brian Goetz wrote: > First of all, when should this rule apply? > > At any point in a constructor (one that successfully compiles), the new > object is always definitively in one of three states: > > State 0: neither super() nor this() has been invoked; neither the > superclass nor this class are initialized > State 1: super() has been invoked, so the superclass is initialized, but > at least one final field in this class has not been initialized > State 2: super() has been invoked and all final fields in this class have > been initialized > > BTW, the current state is easily tracked within the existing DA/DU > analysis of the prototype patch. > > The possible state transitions are: > > - super() takes you from: > - State 0 ? State 1 if there remain any uninitialized final fields > - State 0 ? State 2 if there are no remaining uninitialized final fields > - this() takes you from State 0 ? State 2 > - Initializing the last final field takes you from: > - State 0 ? State 0 > - State 1 ? State 2 > > Presumably the 'this' escape analysis should only apply in State 1. This > is when the object is vulnerable because it's half initialized. > > > In actuality, `this` can escape in any of the states; escaping means that > `this` is exposed before the return from the constructor. (Even the very > last line of the constructor is suspect. A class could have subclasses, > which means we're still in "the middle" of the subclass constructor, and > even for a final class, the write barrier does not get emitted until after > the constructor completes.) > It can't escape in State 0, because the JVM itself prevents that, right? But of course you're right about State 2 - the subclass constructor is still happening at that point, and so you could invoke a method that is overridden in the subclass, but the subclass won't be ready for it yet. In any case, let's start with the strictest view and work backwards to > relaxation. The receiver may escape if: > > - a non-final instance method is invoked (*) > - an instance method declared in a superclass is invoked > - the variable `this` is used for anything other than field access > > We should analyze not only constructor code, but instance initializer > blocks and instance field initializers as well. > Yes... they are just blobs of code that get executed immediately after any super() calls (as I've learned) and the patch includes a similar change to that effect for DA/DU already. > We can exclude final instance methods declared in the same class if the > same analysis of the method (recursively) shows no escape of `this` (this > is the * in the first bullet above.) > Neat. You could also analyze static methods in the class to verify non-escape if they were invoked with 'this' as a parameter... it's more or less the same situation. Any code in the compilation unit that is directly invoked by the constructor and that can't possibly be overridden could in theory be fair game for this non-escape analysis. Inner class creation is a similar story as calling final methods in the > same class; we can do a similar analysis of the inner class constructor to > verify non-escape. > But the inner class Outer.Inner's constructor is going to say this.$0 = Outer.this, which is an escape for Outer.this, right? That would permit sneaky situations like this (choose either lambda or inner class variant): public class MyClass { public MyClass() { new Runnable() { public void run() { MyClass.this.foo(); } }.run(); } public void foo() { // ... } } > For the meaning of "known to be equal to 'this'", we would be limited in > our ability to infer it in the usual ways (Turing complete, blah blah), so > you'd still be able to evade the warning e.g.: > > > For this to happen, `this` must have already escaped or have been assigned > to another variable. The above already detects this. > Right - in other words, the "anything other than field access" clause would disallow casting (Object)this like in the example. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 1 22:58:02 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 1 Nov 2022 18:58:02 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> Message-ID: > We can exclude final instance methods declared in the same class > if the same analysis of the method (recursively) shows no escape > of `this` (this is the * in the first bullet above.) > > Neat. > > You could also analyze static methods in the class to verify > non-escape if they were invoked with 'this' as a parameter... it's > more or less the same situation. > > Any code in the compilation unit that is directly invoked by the > constructor and that can't possibly be overridden could in theory be > fair game for this non-escape analysis. Right, more of the same trick.? The key is not crossing source files, since that is over the analysis horizon. > > Inner class creation is a similar story as calling final methods > in the same class; we can do a similar analysis of the inner class > constructor to verify non-escape. > > > But the inner class Outer.Inner's constructor is going to say this.$0 > = Outer.this, which is an escape for Outer.this, right? Only if the inner class instance escapes.? But we're definitely getting into the "advanced analysis tricks" territory now. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ice1000kotlin at foxmail.com Wed Nov 2 18:59:52 2022 From: ice1000kotlin at foxmail.com (=?utf-8?B?VGVzbGEgWmhhbmc=?=) Date: Wed, 2 Nov 2022 14:59:52 -0400 Subject: Question regarding exhaustiveness check of record patterns Message-ID: Hi, I am confused by a compile error generated by the following code: sealed interface I From forax at univ-mlv.fr Wed Nov 2 19:11:51 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 2 Nov 2022 20:11:51 +0100 (CET) Subject: Question regarding exhaustiveness check of record patterns In-Reply-To: References: Message-ID: <498795487.38472138.1667416311875.JavaMail.zimbra@u-pem.fr> > From: "Tesla Ice Zhang" > To: "amber-dev" > Sent: Wednesday, November 2, 2022 7:59:52 PM > Subject: Question regarding exhaustiveness check of record patterns > Hi, > I am confused by a compile error generated by the following code: > sealed interface I {} > record A(T t) implements I {} > public static void main(I args) { > switch (args) { > case A(var x) -> System.out.println(x); > } > } > Compiling the code with the following JDK: > openjdk 19 2022-09-20 > OpenJDK Runtime Environment (build 19+37) > OpenJDK 64-Bit Server VM (build 19+37, mixed mode, sharing) > I get: > .\JavaBug.java:5: error: the switch statement does not cover all possible input > values > switch (args) { > ^ > Note: .\JavaBug.java uses preview features of Java SE 19. > Note: Recompile with -Xlint:preview for details. > 1 error > What cases could be not covered? It looks like a bug to me. It is a bug, i'm able to reproduce it. TeslaSwitchTest.java:6: error: the switch statement does not cover all possible input values switch (args) { ^ Note: TeslaSwitchTest.java uses preview features of Java SE 20. Note: Recompile with -Xlint:preview for details. 1 error As a workaround, using a type parameter works static void main(I args) { switch (args) { case A(var x) -> System.out.println(x); } } > Also, I believe that showing the particular uncovered cases in the error > messages would make very good developer experiences. yes ! > Best regards, > Tesla R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Nov 3 00:52:33 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Wed, 2 Nov 2022 19:52:33 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> Message-ID: On Tue, Nov 1, 2022 at 5:58 PM Brian Goetz wrote: > Right, more of the same trick. The key is not crossing source files, > since that is over the analysis horizon. > OK so let's assume we can come up with a reasonable definition for "this escape" that is rich enough to be practically useful but not too complex to calculate in the compiler. What next? This new "this escape" warning would really just be a new compiler feature rather than a language change, right? It's not changing the language, it's just expanding the set of possible warnings that can be generated. Obviously it would be very helpful in getting people to think more about "constructor hygiene". So then would its inclusion in a JEP be more about precisely defining the concept and describing the goodness that comes from it? Thanks, -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Nov 3 13:07:15 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 3 Nov 2022 09:07:15 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> Message-ID: <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> > This new "this escape" warning would really just be a new compiler > feature rather than a language change, right? It's not changing the > language, it's just expanding the set of possible warnings that can be > generated. Obviously it would be very helpful in getting people to > think more about "constructor hygiene". Warnings generally do not require a spec change (though few some are specified as "mandatory warnings".)? Obviously changing what statements are allowed where does requires spec change. > So then would its inclusion in a JEP be more about precisely defining > the concept and describing the goodness that comes from it? While we could do more warnings as ordinary RFEs, there's potentially still value in grouping these all under a JEP, for the reasons you hint at and more.? Let's work through the design, figure out if we can actually provide any new safety guarantees (as opposed to just more warnings), and then figure out the best vehicle. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Nov 3 22:50:42 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 3 Nov 2022 17:50:42 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> Message-ID: On Thu, Nov 3, 2022 at 8:07 AM Brian Goetz wrote: > While we could do more warnings as ordinary RFEs, there's potentially > still value in grouping these all under a JEP, for the reasons you hint at > and more. Let's work through the design, figure out if we can actually > provide any new safety guarantees (as opposed to just more warnings), and > then figure out the best vehicle. > Sounds good. I'll try to throw out a starting point... Meta-comment: it's probably prudent to start with something relatively narrow and well-defined. Once that idea is fully understood we can expand from there. OK let's define the problem. IMHO the most basic problem relates to final fields. Obviously, Java is advertising them as special by guaranteeing immutability and safe publication after construction. Yet it's still easy for a final field to be read incorrectly before it's been properly initialized. The FilteredSet example demonstrates this, and also shows how non-obvious it can be. So having compiler automation that would help you avoid this problem makes sense. Ideally we would like to have some warning X for which we can say "If a class A has a constructor that (a) compiles without generating any X warnings, and (b) has no @SuppressWarnings("X") annotation, then the compiler guarantees that no final field in class A can ever be read before it's initialized". Note the narrowness of this: we are only talking about final fields in class A. Not fields in A's superclass or subclasses. We're not talking about "object initialization", we're just talking about individual fields. >From a developer point of view, this is a feature: you can assess (and address) the situation one class and one field at a time. Even so, from a practical point of view, solving this problem is likely to cover the vast majority of the real-world cases where "this escape" causes trouble, at least in my experience. The "this escape" problems I've seen invariably can be solved by moving some final field initialization to before the super() call (which will now be possible), as in the FilteredSet example. There is another subtlety behind this idea: the warning needs to be emitted at the point of offense, so we need to define where that is. Consider the FilteredSet example: which of FilteredSet, HashSet, or AbstractCollection is "at fault"?? Is the problem the fault of the HashSet() constructor, for invoking addAll()? That's a superclass method, not a subclass method, so that seems reasonable. Is it the fault of AbstractCollection.addAll(), which calls add(), which is very likely to be overridden in a subclass? That seems perfectly normal - after all addAll() is a regular method, not a constructor. Or is it the fault of FilteredSet for not initializing the filter field prior to invoking super()? My thought on this is sort of akin to the old saying that "good fences make for good neighbors". In other words, don't rely on others to not overstep when you have a simple way to prevent that yourself. Now that we're giving classes the ability to protect themselves by doing final field initializations prior to super() calls, let's put the burden of protection on them, not on some unrelated superclass. In other words, FilteredSet is "to blame" here. OK so now let's analyze when a final field myFinalField in class MyClass could possibly be read prior to initialization... Consider any path of execution through a constructor. It will invoke either this() or super() exactly once. If it invokes super(), it will also initialize myFinalField at some point, either explicitly, or implicitly - immediately after the super() call if the field has an initializer or is assigned in an initialization block. Observation #1: Prior to invoking this() or super(), it's impossible for any field to be read (JVM guarantee), so clearly it's impossible for myFinalField to be read prior to initialization. No need to check here. Observation #2: If the constructor invokes this(), we assume by induction that the other constructor has already been checked and any warnings generated. Upon its return, myFinalField must be initialized already so we're done here too. So we only need to be concerned with the time during and after a super() call. If myFinalField is explicitly assigned prior to invoking super(), then we're done - it's impossible for there to be any reference prior to initialization by Observation #1. Therefore, we only need to be concerned with the case where myFinalField is initialized after the super() call. Let's call the time from the super() invocation to the point where myFinalField gets initialized the "vulnerability window for myFinalField" and any access to myFinalField within this window an "early access". We want the compiler to generate a warning unless it can prove to itself that there are no early accesses to myFinalField. There are two cases: early accesses during the super() call, and early accesses after the super() call. Case #1 - Early accesses during the super() call We can make a special case here for classes that extend java.lang.Object, where we can assume the superclass constructor does nothing. Done. Otherwise, we observe the following: a superclass constructor could downcast itself and, if myFinalField is public (or if in the same package, any non-private), access it directly. It could also access any public (or if in the same package, any non-private) method of MyClass without necessarily having to downcast - and one of those methods is very likely to be able to access myFinalField because that's very common. So it seems pretty hopeless trying to prove there are no possible early accesses to myFinalField when the superclass is not Object and we can't inspect its constructor. It might be possible if myFinalField were private, and none of MyClass's non-private methods accessed it, even indirectly. Even then it would take work to prove, and this would only help in a few uncommon cases. Hmm... moving on... Case #2 - Early accesses after the super() call The only way myFinalField could be accessed is if the constructor itself does so directly, passes off this to somewhere we can't follow it, or if it (perhaps indirectly) invokes a non-static method of MyClass (or a subclass of MyClass if myFinalField is not private) which contains an early access. Here there may be a little more hope. Let's say an AST subtree is "nested" if it appears inside a lambda or inner class. Let's define these fairly broad/conservative categories of AST subtress that could possibly lead to an early access of myFinalField: 1. Non-nested expressions of the form myFinalField, this.myFinalField, or MyClass.this.myFinalField 2. Non-nested non-static method invocations explicitly targeting this, MyClass.this, super or SomeType.super, or implicitly targeting this instance, e.g., hashCode() 1. Except for private or final methods declared in MyClass that themselves have no early access 3. Non-nested expressions this or MyClass.this that are not part of a field or method access (e.g., method parameters) 4. Non-nested instantiations of any inner class for which MyClass is the outer class 5. Non-nested lambda expressions Notes: - We can define these modulo extraneous parentheses, so for example ((this)).method() would be in category #2 - The "Except for" methods in #2 are defined recursively, but they should still be well-defined. - In constructors, category #1 is already taken care of for us - these expressions already generate a compiler error (once pr#10956 is accepted :) - But not in regular methods, so we'd need checks in that case I *think* every early access after the super() call would have to result from an AST matching one of the above categories. I'll stop here for now... thoughts? -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Nov 4 16:27:08 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 4 Nov 2022 12:27:08 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> Message-ID: <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> I'm not sure "final fields" is the "most basic", but its surely a reasonable problem to work on. A key aspect of this is identifying what code might be run during specific windows of time.? For the window between "super call" and "last final field assigned", we are worried about final field reads; for the window covering the entire ctor, we are worried about broader uses of `this`. We can then analyze such code (or if it is potentially in another compilation unit, issue a warning.) To your point about "the superclass might downcast", this is something you can check for as something questionable to do with `this`. On 11/3/2022 6:50 PM, Archie Cobbs wrote: > On Thu, Nov 3, 2022 at 8:07 AM Brian Goetz wrote: > > While we could do more warnings as ordinary RFEs, there's > potentially still value in grouping these all under a JEP, for the > reasons you hint at and more.? Let's work through the design, > figure out if we can actually provide any new safety guarantees > (as opposed to just more warnings), and then figure out the best > vehicle. > > > Sounds good. > > I'll try to throw out a starting point... > > Meta-comment: it's probably prudent to start with something relatively > narrow and well-defined. Once that idea is fully understood we can > expand from there. > > OK let's define the problem. IMHO the most basic problem relates to > final fields. Obviously, Java is advertising them as special by > guaranteeing immutability and safe publication after construction. Yet > it's still easy for a final field to be read incorrectly before it's > been properly initialized. The FilteredSet example demonstrates this, > and also shows how non-obvious it can be. So having compiler > automation that would help you avoid this problem makes sense. > > Ideally we would like to have some warning X for which we can say "If > a class A has a constructor that (a) compiles without generating any X > warnings, and (b) has no @SuppressWarnings("X") annotation, then the > compiler guarantees that no final field in class A can ever be read > before it's initialized". > > Note the narrowness of this: we are only talking about final fields in > class A. Not fields in A's superclass or subclasses. We're not talking > about "object initialization", we're just talking about individual > fields. From a developer point of view, this is a feature: you can > assess (and address) the situation one class and one field at a time. > > Even so, from a practical point of view, solving this problem is > likely to cover the vast majority of the real-world cases where "this > escape" causes trouble, at least in my experience. The "this escape" > problems I've seen invariably can be solved by moving some final field > initialization to before the super() call (which will now be > possible), as in the FilteredSet example. > > There is another subtlety behind this idea: the warning needs to be > emitted at the point of offense, so we need to define where that is. > > Consider the FilteredSet example: which of FilteredSet, HashSet, or > AbstractCollection is "at fault"?? > > Is the problem the fault of the HashSet() constructor, for invoking > addAll()? That's a superclass method, not a subclass method, so that > seems reasonable. Is it the fault of AbstractCollection.addAll(), > which calls add(), which is very likely to be overridden in a > subclass? That seems perfectly normal - after all addAll() is a > regular method, not a constructor. Or is it the fault of FilteredSet > for not initializing the filter field prior to invoking super()? > > My thought on this is sort of akin to the old saying that "good fences > make for good neighbors". In other words, don't rely on others to not > overstep when you have a simple way to prevent that yourself. Now that > we're giving classes the ability to protect themselves by doing final > field initializations prior to super() calls, let's put the burden of > protection on them, not on some unrelated superclass. In other words, > FilteredSet is "to blame" here. > > OK so now let's analyze when a final field myFinalField in class > MyClass could possibly be read prior to initialization... > > Consider any path of execution through a constructor. It will invoke > either this() or super() exactly once. If it invokes super(), it will > also initialize myFinalField at some point, either explicitly, or > implicitly - immediately after the super() call if the field has an > initializer or is assigned in an initialization block. > > Observation #1: Prior to invoking this() or super(), it's impossible > for any field to be read (JVM guarantee), so clearly it's impossible > for myFinalField to be read prior to initialization. No need to check > here. > > Observation #2: If the constructor invokes this(), we assume by > induction that the other constructor has already been checked and any > warnings generated. Upon its return, myFinalField must be initialized > already so we're done here too. > > So we only need to be concerned with the time during and after a > super() call. > > If myFinalField is explicitly assigned prior to invoking super(), then > we're done - it's impossible for there to be any reference prior to > initialization by Observation #1. > > Therefore, we only need to be concerned with the case where > myFinalField is initialized after the super() call. > > Let's call the time from the super() invocation to the point where > myFinalField gets initialized the "vulnerability window for > myFinalField" and any access to myFinalField within this window an > "early access". > > We want the compiler to generate a warning unless it can prove to > itself that there are no early accesses to myFinalField. > > There are two cases: early accesses during the super() call, and early > accesses after the super() call. > > Case #1 - Early accesses during the super() call > > We can make a special case here for classes that extend > java.lang.Object, where we can assume the superclass constructor does > nothing. Done. > > Otherwise, we observe the following: a superclass constructor could > downcast itself and, if myFinalFieldis public (or if in the same > package, any non-private), access it directly. It could also access > any public (or if in the same package, any non-private) method of > MyClass without necessarily having to downcast - and one of those > methods is very likely to be able to access myFinalFieldbecause that's > very common. > > So it seems pretty hopeless trying to prove there are no possible > early accesses to myFinalField when the superclass is not Object and > we can't inspect its constructor. It might be possible if > myFinalFieldwere private, and none of MyClass's non-private methods > accessed it, even indirectly. Even then it would take work to prove, > and this would only help in a few uncommon cases. > > Hmm... moving on... > > Case #2 - Early accesses after the super() call > > The only way myFinalField could be accessed is if the constructor > itself does so directly, passes off this to somewhere we can't follow > it, or if it (perhaps indirectly) invokes a non-static method of > MyClass (or a subclass of MyClass if myFinalFieldis not private) which > contains an early access. > > Here there may be a little more hope. > > Let's say an AST subtree is "nested" if it appears inside a lambda or > inner class. > > Let's define these fairly broad/conservative categories of AST > subtress that could possibly lead to an early access of myFinalField: > > 1. Non-nested expressions of the form myFinalField, > this.myFinalField, or MyClass.this.myFinalField > 2. Non-nested non-static method invocations explicitly targeting > this, MyClass.this, super or SomeType.super, or implicitly > targeting this instance, e.g., hashCode() > 1. Except for private or final methods declared in MyClass that > themselves have no early access > 3. Non-nested expressions this or MyClass.thisthat are not part of a > field or method access (e.g., method parameters) > 4. Non-nested instantiations of any inner class for which MyClass is > the outer class > 5. Non-nested lambda expressions > > Notes: > > * We can define these modulo extraneous parentheses, so for example > ((this)).method()would be in category #2 > * The "Except for" methods in #2 are defined recursively, but they > should still be well-defined. > * In constructors, category #1 is already taken care of for us - > these expressions already generate a compiler error (once pr#10956 > > is accepted :) > o But not in regular methods, so we'd need checks in that case > > I *think* every early access after the super() call would have to > result from an AST matching one of the above categories. > > I'll stop here for now... thoughts? > > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Nov 4 16:48:49 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 4 Nov 2022 11:48:49 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> Message-ID: On Fri, Nov 4, 2022 at 11:27 AM Brian Goetz wrote: > A key aspect of this is identifying what code might be run during specific > windows of time. For the window between "super call" and "last final field > assigned", we are worried about final field reads; for the window covering > the entire ctor, we are worried about broader uses of `this`. > Good point - they are really two separate aspects of the overall "problem". So perhaps there are two separate warnings we should be generating, each with their own independent analysis/logic. Doing this would also allow us to spread the "blame" more equitably. So with the FilteredSet example: - FilteredSet's constructor should generate warning A for not assigning this.filter prior to super() - HashSet's constructor should generate warning B for invoking the potentially this-leaking method addAll() This sounds like the right logical separation. One confirmation is that correcting *either one* of the warnings would fix the bug. So each class must be independently contributing to the overall danger level. > To your point about "the superclass might downcast", this is something you > can check for as something questionable to do with `this`. > Agreed - this should also generate warning B, because it represents a possible route for 'this' to escape. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Nov 4 17:13:50 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 4 Nov 2022 13:13:50 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> Message-ID: > So with the FilteredSet example: > > * FilteredSet's constructor should generate warning A for not > assigning this.filter prior to super() > * HashSet's constructor should generate warning B for invoking the > potentially this-leaking method addAll() > I'm not about that direction for the FilteredSet example.? The basic problem is that HashSet's constructor calls an overridable method, addAll, which in turn invokes the add override in FilteredSet.? This is a this-escape (even if filter is initialized before super, because FilteredSet could be subclassed and override add.) The conclusion that "the problem would go away if we could set the filter first" merely moves the problem around. > This sounds like the right logical separation. One confirmation is > that correcting /either one/ of the warnings would fix the bug. So > each class must be independently contributing to the overall danger level. > > To your point about "the superclass might downcast", this is > something you can check for as something questionable to do with > `this`. > > > Agreed - this should also generate warning B, because it represents a > possible route for 'this' to escape. > > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Nov 4 17:38:43 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 4 Nov 2022 12:38:43 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: References: <11B5833E-2B52-4174-8608-6B0373D6A2A5@oracle.com> <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> Message-ID: On Fri, Nov 4, 2022 at 12:13 PM Brian Goetz wrote: > > So with the FilteredSet example: > > - FilteredSet's constructor should generate warning A for not > assigning this.filter prior to super() > - HashSet's constructor should generate warning B for invoking the > potentially this-leaking method addAll() > > > I'm not about that direction for the FilteredSet example. The basic > problem is that HashSet's constructor calls an overridable method, addAll, > which in turn invokes the add override in FilteredSet. This is a > this-escape (even if filter is initialized before super, because > FilteredSet could be subclassed and override add.) > > The conclusion that "the problem would go away if we could set the filter > first" merely moves the problem around. > Right. I agree that HashSet's constructor invoking addAll() is its own problem, and should generate a warning on its own. So I guess the question is: in the case of FilteredSet, should we generate a warning simply because we know there exist this-leaking superclasses out there, and HashSet might be one of them? It's like warning someone not to leave their doors unlocked. If a crime occurs it's not their fault, but the warning might be useful nonetheless. On the other hand, such a warning would not be practical: today it would flag every class not directly extending java.lang.Object that has any final fields! A better answer would be a "best effort" warning that would only occur if the compiler were able to actually observe this-leaking behavior in the superclass. Not sure how practical that is to implement though. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Nov 4 18:07:57 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 4 Nov 2022 14:07:57 -0400 Subject: Loosening requirements for super() invocation In-Reply-To: References: <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> Message-ID: <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> We should check each file locally, without regard to whether superclasses or subclasses are problematic. On 11/4/2022 1:38 PM, Archie Cobbs wrote: > On Fri, Nov 4, 2022 at 12:13 PM Brian Goetz > wrote: > > >> So with the FilteredSet example: >> >> * FilteredSet's constructor should generate warning A for not >> assigning this.filter prior to super() >> * HashSet's constructor should generate warning B for invoking >> the potentially this-leaking method addAll() >> > > I'm not about that direction for the FilteredSet example. The > basic problem is that HashSet's constructor calls an overridable > method, addAll, which in turn invokes the add override in > FilteredSet.? This is a this-escape (even if filter is initialized > before super, because FilteredSet could be subclassed and override > add.) > > The conclusion that "the problem would go away if we could set the > filter first" merely moves the problem around. > > > Right. I agree that HashSet's constructor invoking addAll() is its own > problem, and should generate a warning on its own. > > So I guess the question is: in the case of FilteredSet, should we > generate a warning simply because we know there exist this-leaking > superclasses out there, and HashSet might be one of them? It's like > warning someone not to leave their doors unlocked. If a crime occurs > it's not their fault, but the warning might be useful nonetheless. > > On the other hand, such a warning would not be practical: today it > would flag every class not directly extending java.lang.Object that > has any final fields! > > A better answer would be a "best effort" warning that would only occur > if the compiler were able to actually observe this-leaking behavior in > the superclass. Not sure how practical that is to implement though. > > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Nov 4 22:59:13 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 4 Nov 2022 17:59:13 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> References: <72c76b30-43e8-e9c1-5321-e2a54d242008@oracle.com> <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> Message-ID: On Fri, Nov 4, 2022 at 1:08 PM Brian Goetz wrote: > We should check each file locally, without regard to whether superclasses > or subclasses are problematic. > That definitely keeps things simpler. So it sounds like we have boiled this down to the following: 1. Add one new warning, which applies to one class at a time 2. The warning looks for constructors with a possible 'this' escape Obviously this is a "best effort" thing, false positives will happen. The warning would say something like "possible early access before instance is fully initialized". Now we just need a definition of "possible 'this' escape". The goal is to close off all routes by which 'this' could end up being passed to code in any subclass. In other words we are not trying to save the class from itself. Attempt #1... A 'this' escape is when, in a non-final class MyClass constructor, after a super() call, a reference to the 'this' instance is used, explicitly or implicitly, in any expression that (as far as the compiler can tell) might possibly: 1. Invoke a non-static method declared in any strict supertype of MyClass 2. Invoke a non-static, non-private, non-final method declared in MyClass 3. Invoke a non-static, private or final method declared in MyClass that has a possible 'this' escape (recursive definition) 4. Otherwise access a field or invoke a non-static method declared in any subclass of MyClass Any expression caught by #4 would necessarily involve a downcast, so the compiler might choose to simply check for any expression that could possibly downcast 'this'. Test case 1: HashSet should generate a warning. It does because of case #1. Test case 2: the class below should not generate a warning. It doesn't because resetBoard() is private and contains no 'this' escapes. public class TicTacToe { private final char[][] board = new char[3][3]; public TicTacToe() { this.resetBoard(); } private void resetBoard() { Stream.of(this.board).forEach(row -> Arrays.fill(row, ' ')); } // more stuff here... } Test case 3: FilteredSet does not generate a warning. Instead, we are putting the "blame" on HashSet. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 8 21:00:23 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 8 Nov 2022 16:00:23 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: References: <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> Message-ID: > > The goal is to close off all routes by which 'this' could end up being > passed to code in any subclass. any other class outside this compilation unit: subclass, superclass, random class.? This is sometimes called "alien" code -- code this class does not control.? (Note that inner classes in the current class are OK; it's under this classes control.) > Attempt #1... > > A 'this' escape is when, in a non-final class MyClass constructor, > after a super() call, a reference to the 'this' instance is used, > explicitly or implicitly, in any expression that (as far as the > compiler can tell) might possibly: > > 1. Invoke a non-static method declared in any strict supertype of MyClass > Declared outside the compilation unit of MyClass > 1. Otherwise access a field or invoke a non-static method declared in > any subclass of MyClass > Plus: 5.? Store `this` to any variable. > Test case 2: the class below should not generate a warning. It doesn't > because resetBoard() is private and contains no 'this' escapes. > > public class TicTacToe { > > ??? private final char[][] board = new char[3][3]; > > ??? public TicTacToe() { > ??????? this.resetBoard(); > ??? } > > ??? private void resetBoard() { > Stream.of(this.board).forEach(row -> Arrays.fill(row, ' ')); > ??? } > > ??? // more stuff here... > } > > Test case 3: FilteredSet does not generate a warning. Instead, we are > putting the "blame" on HashSet. > Here's a more advanced case that would require more sophisticated data flow analysis, but might not be so bad: public class X { ??? public X() { foo(this); } ??? private static void foo(X x) {? } } Here, you pass `this` to foo(), but it doesn't do anything bad with it. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Wed Nov 9 15:54:45 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Wed, 9 Nov 2022 09:54:45 -0600 Subject: Loosening requirements for super() invocation In-Reply-To: References: <6bdabf6f-c412-46fe-d00c-1e328afb10d5@oracle.com> <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> Message-ID: On Tue, Nov 8, 2022 at 3:00 PM Brian Goetz wrote: > > The goal is to close off all routes by which 'this' could end up being > passed to code in any subclass. > > any other class outside this compilation unit: subclass, superclass, > random class. This is sometimes called "alien" code -- code this class > does not control. (Note that inner classes in the current class are OK; > it's under this classes control.) > Yes I think we're saying the same thing - passing 'this' to any "alien" code is a leak. I'm just pointing out that the damage doesn't actually occur until if/when the 'this' is actually used to access state in a subclass. But that's just a technical point and doesn't ultimately change the kinds of expressions we have to watch out for. > A 'this' escape is when, in a non-final class MyClass constructor, after > a super() call, a reference to the 'this' instance is used, explicitly or > implicitly, in any expression that (as far as the compiler can tell) might > possibly: > > 1. Invoke a non-static method declared in any strict supertype of > MyClass > > > Declared outside the compilation unit of MyClass > Agreed... that formulation is simpler. But of course it would need a caveat for subclasses within the same compilation unit, e.g.: public class Example { public static abstract class A { public A() { System.out.println(this.foo()); } public abstract int foo(); } public static class B extends A { private final int x; public B(int x) { this.x = x; } public int foo() { return this.x; } } public static void main(String[] args) { new B(123); // prints "0" } } > > Here's a more advanced case that would require more sophisticated data > flow analysis, but might not be so bad: > > public class X { > public X() { foo(this); } > > private static void foo(X x) { } > } > > Here, you pass `this` to foo(), but it doesn't do anything bad with it. > Yeah, that would be harder to track but feasible. I'd need to think about how to do that properly. However I should be able to come up with an initial prototype for the basic 'this' escape warning without too much work. I'll email when I have something. Thanks, -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Nov 9 16:07:27 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 9 Nov 2022 11:07:27 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: References: <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> Message-ID: <7242cdba-0b7d-2520-b159-b13f91113c18@oracle.com> > > Yes I think we're saying the same thing - passing 'this' to any > "alien" code is a leak. > > I'm just pointing out that the damage doesn't actually occur until > if/when the 'this' is actually used to access state in a subclass. But > that's just a technical point and doesn't ultimately change the kinds > of expressions we have to watch out for. This is similar to the story with heap pollution; the damage of putting an `Integer` into a `List` doesn't happen until you try to take it out.? We warn at the points where pollution *might* happen. > Declared outside the compilation unit of MyClass > > Agreed... that formulation is simpler. But of course it would need a > caveat for subclasses within the same compilation unit, e.g.: > > public class Example { > > ? ? public static abstract class A { > > ? ? ? ? public A() { > ? ? ? ? ? ? System.out.println(this.foo()); > ? ? ? ? } > > ? ? ? ? public abstract int foo(); > ? ? } > > ? ? public static class B extends A { > > ? ? ? ? private final int x; > > ? ? ? ? public B(int x) { > ? ? ? ? ? ? this.x = x; > ? ? ? ? } > > ? ? ? ? public int foo() { > ? ? ? ? ? ? return this.x; > ? ? ? ? } > ? ? } > > ? ? public static void main(String[] args) { > ? ? ? ? new B(123); ? ? // prints "0" > ? ? } > } All these classes are in the same compilation unit, though it's possible you may not want to get fancy enough with the analysis to simulate virtual dispatch (though you could, since its all in the same file.) -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Wed Nov 9 16:54:04 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Wed, 9 Nov 2022 10:54:04 -0600 Subject: Loosening requirements for super() invocation In-Reply-To: <7242cdba-0b7d-2520-b159-b13f91113c18@oracle.com> References: <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> <7242cdba-0b7d-2520-b159-b13f91113c18@oracle.com> Message-ID: On Wed, Nov 9, 2022 at 10:38 AM Brian Goetz wrote: > > Agreed... that formulation is simpler. But of course it would need a > caveat for subclasses within the same compilation unit, e.g.: > > > All these classes are in the same compilation unit, though it's possible > you may not want to get fancy enough with the analysis to simulate virtual > dispatch (though you could, since its all in the same file.) > OK so it sounds like you're saying that it's not worth bothering trying to prevent someone from a 'this' escape that is contained within a single source file, because those cases are relatively easy ones to spot. Rather, it's in the non-obvious cases, where the problem spans multiple compilation units, that programmers could really benefit from some extra help from the compiler. This defines a clear & straightforward boundary for the warning. -Archie -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Nov 9 17:00:02 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 9 Nov 2022 12:00:02 -0500 Subject: Loosening requirements for super() invocation In-Reply-To: References: <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> <7242cdba-0b7d-2520-b159-b13f91113c18@oracle.com> Message-ID: <347917af-4663-6fa8-a6b3-6645a3e5f5ce@oracle.com> There's a few things you could mean by "not worth bothering" here. For things within a source file, we can detect when the use of `this` is safe, as in: class Foo { ??? Foo() { m(this); } ??? private void m(Foo f) { /* nothing */ } } and so we could elide warnings for cases like this if we wanted to. But in general, what I'm saying is that we should warn when we detect there is a *possibility* of a problem, rather than having to prove that there *will be* a problem. The HashMap example is really the kind of code that we want to give users feedback on, because people make dodgy self-use from constructors all the time, and don't realize they are setting a time bomb.? Similarly, people enqueue listeners from constructors, and if the listener gets called too early ... boom.? These are the things we want to detect.? The rest, like the above examples, is trimming away false positives. On 11/9/2022 11:54 AM, Archie Cobbs wrote: > On Wed, Nov 9, 2022 at 10:38 AM Brian Goetz > wrote: > > >> Agreed... that formulation is simpler. But of course it would >> need a caveat for subclasses within the same compilation unit, e.g.: > > All these classes are in the same compilation unit, though it's > possible you may not want to get fancy enough with the analysis to > simulate virtual dispatch (though you could, since its all in the > same file.) > > > OK so it sounds like you're saying that it's not worth bothering > trying to prevent someone from a 'this' escape that is contained > within a single source file, because those cases are relatively easy > ones to spot. > > Rather, it's in the non-obvious cases, where the problem spans > multiple compilation units, that programmers could really benefit from > some extra help from the compiler. > > This defines a clear & straightforward boundary for the warning. > > -Archie -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Wed Nov 9 18:06:45 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Wed, 9 Nov 2022 12:06:45 -0600 Subject: Loosening requirements for super() invocation In-Reply-To: <347917af-4663-6fa8-a6b3-6645a3e5f5ce@oracle.com> References: <140e133e-4c46-5d83-1ccf-32e40406a13a@oracle.com> <362d1d7b-97fe-dc2e-de02-1d0169bd2626@oracle.com> <75cc216c-12c1-d7bd-37f1-c73d4fc97143@oracle.com> <7242cdba-0b7d-2520-b159-b13f91113c18@oracle.com> <347917af-4663-6fa8-a6b3-6645a3e5f5ce@oracle.com> Message-ID: On Wed, Nov 9, 2022 at 11:23 AM Brian Goetz wrote: > what I'm saying is that we should warn when we detect there is a > *possibility* of a problem, rather than having to prove that there *will > be* a problem. > Totally agree... we can allow false positives but definitely not false negatives. That's what allows a programmer to say "I've eliminated all the warnings and so now I KNOW this code can't cause a 'this' escape problem". As you pointed out, since we can't assume anything about "alien" code, any clever tricks to eliminate false positives are limited to what we can accomplish by analyzing a single source file. Other than that, which false positives we want to hunt down and eliminate is just a standard trade-off between effort required and value provided. I'll try to come up with a concrete but simple starting point. We can then discuss whether & how it should be more clever re: false positives. Thanks, -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From ice1000kotlin at foxmail.com Thu Nov 10 14:32:52 2022 From: ice1000kotlin at foxmail.com (=?utf-8?B?VGVzbGEgWmhhbmc=?=) Date: Thu, 10 Nov 2022 10:32:52 -0400 Subject: Question regarding exhaustiveness check of record patterns Message-ID: Is this bug going to be fixed?  Regards, Tesla ---Original--- From: "Remi Forax" From jarabekit at gmail.com Sat Nov 12 02:36:48 2022 From: jarabekit at gmail.com (Daniel Jarabek) Date: Fri, 11 Nov 2022 21:36:48 -0500 Subject: [string-templates] Automatically imported template processors Message-ID: <5c870010-1862-cb03-4c4c-e54b34b13658@gmail.com> Hi, JEP-430 states that "FMT is a public static final automatically imported in every Java source file". However, this seems to contradict the javadoc of ValidatingProcessor, which says "The Java compiler automatically imports StringTemplate#STR" and the current implementation, which only automatically imports STR. I assume this was a change made because FMT is more subjective (it uses Locale.ROOT, which isn't always what is desired) so users should explicitly choose to use it or use their own instance with a different Locale. Assuming this is an intentional change, the incorrect statement should be removed from the JEP. -DJ P.S. I am working on a TemplateProcessor for constructing JavaPoet's (https://github.com/square/javapoet) CodeBlock object. Would it be appropriate and/or appreciated if I posted my results/experiences here? From james.laskey at oracle.com Sat Nov 12 02:58:59 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Sat, 12 Nov 2022 02:58:59 +0000 Subject: [string-templates] Automatically imported template processors In-Reply-To: <5c870010-1862-cb03-4c4c-e54b34b13658@gmail.com> References: <5c870010-1862-cb03-4c4c-e54b34b13658@gmail.com> Message-ID: <491D389A-05F9-48D8-A055-19445DBC8608@oracle.com> You are correct. I will update the JEP. I think it would be fine for you to post your experiences. I think that is in the spirit of the open process. ? > On Nov 11, 2022, at 10:36 PM, Daniel Jarabek wrote: > > ?Hi, > > JEP-430 states that "FMT is a public static final automatically imported in every Java source file". However, this seems to contradict the javadoc of ValidatingProcessor, which says "The Java compiler automatically imports StringTemplate#STR" and the current implementation, which only automatically imports STR. > > I assume this was a change made because FMT is more subjective (it uses Locale.ROOT, which isn't always what is desired) so users should explicitly choose to use it or use their own instance with a different Locale. > > Assuming this is an intentional change, the incorrect statement should be removed from the JEP. > > -DJ > > P.S. I am working on a TemplateProcessor for constructing JavaPoet's (https://github.com/square/javapoet) CodeBlock object. Would it be appropriate and/or appreciated if I posted my results/experiences here? From numeralnathan at gmail.com Sat Nov 12 04:47:10 2022 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Fri, 11 Nov 2022 21:47:10 -0700 Subject: Throwing Functions Message-ID: I am sorry if I am very late to the discussion. Consumer, Function, Predicate, and Supplier don't allow for throwing checked exceptions. This feature is needed in many cases. What about adding a variant that allows for throwing checked exceptions? For example, a ThrowableConsumer could be defined as such... public interface ThrowableConsumer { void accept(T t) throws E; } A method that receives this as a parameter could be defined as such... public void process(ThrowableConsumer consume) throws E { ... } The compiler takes care of ensuring the checked exception is dealt with in the caller to process(). -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Nov 12 07:58:57 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 12 Nov 2022 08:58:57 +0100 (CET) Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: Message-ID: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> [promoted to amber-spec-experts] I think we should tackle this problem at its core and retire the notion of checked exceptions The written are on the wall since quite some time, but we have not yet acknowledge it. Let's recap the issue and the clues pointing in that direction. I believe the first straw can be tracked back to the inability of the type system to create a type variable containing the union of exceptions when generics where introduced in Java 5. It's the reason why there is no ThrowableConsumer, as an example, imagine that a stream can declare a checked exception Stream and a user call map() that takes a ThrowableFunction, we need a way to express that the resulting stream is that combine the exceptions of the original stream and the exceptions that may be raised by calling the function of map. Stream --> map(ThrowableFunction) --> Stream Or perhaps from the beginning of Java, adding a checked exception to a "throws" colors the function the same way the keyword async or Rust lifetime type information color a function [1]. Adding a checked exception to a function is not a backward compatible change. So we have some of idioms that beginners need to internalize to try workaround checked exceptions, - The oldest is i think, Thread.currentThread().interrupt() which allows to silence an InterruptedException at the price of continuing to execute the code until the next blocking call - UncheckedIOException and IOError that both wrap an IOException into an unchecked exception allowing tunneling of checked exceptions ; wrapping an IOException and unbundle it later. - Unsafe.rethrow (this one was retired) - the unfamous rethrow using erasure to see a checked exception as an unchecked exception static AssertionError rethrow(Throwable cause) throws T { throw (T) cause; } - IntelliJ has changed the default try/catch snippet to instead of calling printStackTrace() to throw a RuntimeException wrapping the exception. This simple change is i believe the best change to Java in the recent years (perhaps toes to toes with records), at least now the code of my students does not print the stack trace and resume its course when an exception occurs. try { ... } catch(FooException e) { throw new RuntimeException(e); } Also no language presented as potential successor of Java, neither Scala nor Kotlin have checked exceptions, because functions with checked exceptions do not compose. If Java wants to be the next Java, we will have to drop checked exceptions at some point. The good news is that seeing all exceptions as unchecked exceptions is a backward compatible change, "throws" can still be supported for documentation purpose, the compiler can emit a warning instead of an error if there is no catch corresponding to a checked exception and allow everyone to catch any exceptions in the code. I think we should recognize that the idea of checked exceptions was a good idea on paper but not a good one in practice and work to retire the concept of checked exceptions from Java. R?mi [1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ > From: "Nathan Reynolds" > To: "amber-dev" > Sent: Saturday, November 12, 2022 5:47:10 AM > Subject: Throwing Functions > I am sorry if I am very late to the discussion. Consumer, Function, Predicate, > and Supplier don't allow for throwing checked exceptions. This feature is > needed in many cases. What about adding a variant that allows for throwing > checked exceptions? For example, a ThrowableConsumer could be defined as > such... > public interface ThrowableConsumer > { > void accept(T t) throws E; > } > A method that receives this as a parameter could be defined as such... > public void process(ThrowableConsumer consume) > throws E > { > ... > } > The compiler takes care of ensuring the checked exception is dealt with in the > caller to process(). -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Nov 12 15:36:45 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 12 Nov 2022 10:36:45 -0500 Subject: Throwing Functions In-Reply-To: References: Message-ID: <8766dedf-9933-1d24-4ee0-a4603b9baac7@oracle.com> (corelibs-dev is likely a better place for this, since its not about language.) This was discussed when we did Lambda initially.? There were a few reasons we didn't go there: ?- Poor interactions between generics and exceptions; ?- Exceptions and serialization each "wanted" their own versions of the functional interfaces; a 4x explosion in java.util.function is not so pretty, and doing an ad-hoc "lets just do Function and Consumer" will invariably result in an endless stream of "please add ExceptionalFooBar" requests. It's some years later, so its possible we might reconsider; corelibs-dev is the right place to ask this. On 11/11/2022 11:47 PM, Nathan Reynolds wrote: > I am sorry if I am very late to the discussion.? Consumer, Function, > Predicate, and Supplier don't allow for throwing checked exceptions.? > This feature is needed in many cases. What about adding a variant that > allows for throwing checked exceptions?? For example, a > ThrowableConsumer could be defined as such... > > public interface ThrowableConsumer > { > ?? void accept(T t) throws E; > } > > A method that receives this as a parameter could be defined as such... > > public void process(ThrowableConsumer > consume) throws E > { > ?? ... > } > > The compiler takes care of ensuring the checked exception is dealt > with in the caller to process(). > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Nov 12 15:44:32 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 12 Nov 2022 10:44:32 -0500 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: <86271a04-6d41-02aa-a13c-0426401c4e91@oracle.com> Counter-proposal: let's just retire the endless stream of "let's just get rid of checked exceptions" proposals instead. This is all well-traveled ground, and has all been brought up before (including by you several times.)? I get how attractive it is to "just" declare checked exceptions a mistake.? But even if it was, we know that trying to "fix" the "mistakes" of the past often only makes things worse. There is interesting research going on in the Scala community and elsewhere about better treatment of effects; some of this may bear fruit that might be a better approach than the "just uncheck them all."? In any case, I'm not interested in reopening this discussion at this time; we have plenty else on the priority list. Verdict: Motion Denied.? Leave is granted to refile your motion in 2025. On 11/12/2022 2:58 AM, Remi Forax wrote: > [promoted to amber-spec-experts] > > I think we should tackle this problem at its core and retire the > notion of checked exceptions > > The written are on the wall since quite some time, but we have not yet > acknowledge it. > > Let's recap the issue and the clues pointing in that direction. > > I believe the first straw can be tracked back to the inability of the > type system to create a type variable containing the union of > exceptions when generics where introduced in Java 5. > It's the reason why there is no ThrowableConsumer, as an example, > imagine that a stream can declare a checked exception Stream and > a user call map() that takes a ThrowableFunction, > we need a way to express that the resulting stream is that combine the > exceptions of the original stream and the exceptions that may be > raised by calling the function of map. > > ? Stream? --> map(ThrowableFunction extends Exception>) -->? Stream > > Or perhaps from the beginning of Java, adding a checked exception to a > "throws" colors the function the same way the keyword async or Rust > lifetime type information color a function [1]. Adding a checked > exception to a function is not a backward compatible change. > > So we have some of idioms that beginners need to internalize to try > workaround checked exceptions, > - The oldest is i think, Thread.currentThread().interrupt() which > allows to silence an InterruptedException at the price of continuing > to execute the code until the next blocking call > - UncheckedIOException and IOError that both wrap an IOException into > an unchecked exception allowing tunneling of checked exceptions ; > wrapping an IOException and unbundle it later. > - Unsafe.rethrow (this one was retired) > - the unfamous rethrow using erasure to see a checked exception as an > unchecked exception > ?? static AssertionError rethrow(Throwable > cause) throws T { > ????? throw (T) cause; > ?? } > - IntelliJ has changed the default try/catch snippet to instead of > calling printStackTrace() to throw a RuntimeException wrapping the > exception. > ? This simple change is i believe the best change to Java in the > recent years (perhaps toes to toes with records), at least now the > code of my students does not print the stack trace and resume its > course when an exception occurs. > ??? try { > ???? ... > ??? } catch(FooException e) { > ??? ? throw new RuntimeException(e); > ??? } > > Also no language presented as potential successor of Java, neither > Scala nor Kotlin have checked exceptions, because functions with > checked exceptions do not compose. > If Java wants to be the next Java, we will have to drop checked > exceptions at some point. > > The good news is that seeing all exceptions as unchecked exceptions is > a backward compatible change, "throws" can still be supported for > documentation purpose, the compiler can emit a warning instead of an > error if there is no catch corresponding to a checked exception and > allow everyone to catch any exceptions in the code. > > I think we should recognize that the idea of checked exceptions was a > good idea on paper but not a good one in practice and work to retire > the concept of checked exceptions from Java. > > R?mi > > [1] > https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ > > ------------------------------------------------------------------------ > > *From: *"Nathan Reynolds" > *To: *"amber-dev" > *Sent: *Saturday, November 12, 2022 5:47:10 AM > *Subject: *Throwing Functions > > I am sorry if I am very late to the discussion. Consumer, > Function, Predicate, and Supplier don't allow for throwing checked > exceptions.? This feature is needed in many cases.? What about > adding a variant that allows for throwing checked exceptions?? For > example, a ThrowableConsumer could be defined as such... > > public interface ThrowableConsumer > { > ?? void accept(T t) throws E; > } > > A method that receives this as a parameter could be defined as such... > > public void process(ThrowableConsumer > consume) throws E > { > ?? ... > } > > The compiler takes care of ensuring the checked exception is dealt > with in the caller to process(). > -------------- next part -------------- An HTML attachment was scrubbed... URL: From hjohn at xs4all.nl Sat Nov 12 16:16:10 2022 From: hjohn at xs4all.nl (John Hendrikx) Date: Sat, 12 Nov 2022 16:16:10 +0000 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: I very much disagree with this position, so I will make a case for checked exceptions: 1) Without checked exceptions, using exceptions for alternative outcomes as part of the contract of an API would no longer be possible. This is because adding a new exception to the API would no longer be a breaking change. Documentation is not going to save you here. Effectively, exceptions would have only two uses left (like in other languages): fatal JVM breaking errors and to signal mistakes that could have been prevented by the programmer. Using them to signal unpreventable errors would be highly suspect and likely quickly be considered a bad practice. In other languages (but also in some Java API's that use runtime exception to signal unpreventable errors) the compiler cannot help you with these alternative outcomes as it isn't even required to specify them as part of your API. It helps you with the return value, but not for any other outcomes. These other outcomes will now have to guessed, gleaned from the documentation (if it is up to date), or by doing manual code mining (of the called function and all the functions it calls). Worse, these outcomes can change in later releases without any warning as they're non-breaking changes. 2) API's without checked exceptions will necessarily become more cluttered than their counterparts with checked exceptions. They would have to return wrappers or unions or special values to enforce proper use. API design like this prevents easily deferring the handling of rare occurrences higher up the call chain with standard idioms to keep the current code path clean and to the point. 3) Removing checked exceptions now would break the **design** of many current API's, making them dangerous to use without the checks and balances that were present when they were designed. 4) Earlier design decisions to exclude checked exceptions from new idioms and API's were a mistake that should be rectified not doubled down upon. A large part of the momentum against checked exceptions comes from the clumsy way that checked exceptions must be handled while using these new features that were essentially incompatible with existing Java. Even with the current status quo, it is possible to design API's that do allow checked exceptions in combination with functional programming, it is just cumbersome to design these API's without a dedicated union type (for exceptions). They either support only one checked exception or some limited number. Their use is however as transparent as API's that do not support checked exceptions. 5) The fact that the value of checked exceptions is hard to recognize or cumbersome for beginners or non-Java developers should not have any bearing on language design. Checked exceptions differentiate between the common preventable programming errors and essential business logic. The difference between a runtime exception (leaving out Error) and a checked exception is that one can only occur in incorrect code, while the other can occur even in correct code. One indicates a preventable programming mistake and the other a missed path in your logic. Throwing these on the same heap will effectively reduce the usefulness of all exceptions to programming mistakes (and fatal errors) only. 6) If anything, I think there should be way to promote runtime exceptions to checked exceptions within certain modules. This would allow API / library designers the option to have the compiler check their code for missed documentation and missing logic even for what externally would be a runtime exception. This would be especially useful during initial design and also later refactoring as the incredibly useful compiler checks for checked exceptions could be extended to proper use and propagation of runtime exceptions within that module. --John ------ Original Message ------ >From "Remi Forax" To "Nathan Reynolds" Cc "amber-dev" ; "amber-spec-experts" ; "Ron Pressler" Date 12/11/2022 08:58:57 Subject Retiring Checked Exceptions Was: Throwing Functions >[promoted to amber-spec-experts] > >I think we should tackle this problem at its core and retire the notion >of checked exceptions > >The written are on the wall since quite some time, but we have not yet >acknowledge it. > >Let's recap the issue and the clues pointing in that direction. > >I believe the first straw can be tracked back to the inability of the >type system to create a type variable containing the union of >exceptions when generics where introduced in Java 5. >It's the reason why there is no ThrowableConsumer, as an example, >imagine that a stream can declare a checked exception Stream and >a user call map() that takes a ThrowableFunction, >we need a way to express that the resulting stream is that combine the >exceptions of the original stream and the exceptions that may be raised >by calling the function of map. > > Stream --> map(ThrowableFunctionextends Exception>) --> Stream > >Or perhaps from the beginning of Java, adding a checked exception to a >"throws" colors the function the same way the keyword async or Rust >lifetime type information color a function [1]. Adding a checked >exception to a function is not a backward compatible change. > >So we have some of idioms that beginners need to internalize to try >workaround checked exceptions, >- The oldest is i think, Thread.currentThread().interrupt() which >allows to silence an InterruptedException at the price of continuing to >execute the code until the next blocking call >- UncheckedIOException and IOError that both wrap an IOException into >an unchecked exception allowing tunneling of checked exceptions ; >wrapping an IOException and unbundle it later. >- Unsafe.rethrow (this one was retired) >- the unfamous rethrow using erasure to see a checked exception as an >unchecked exception > static AssertionError rethrow(Throwable cause) >throws T { > throw (T) cause; > } >- IntelliJ has changed the default try/catch snippet to instead of >calling printStackTrace() to throw a RuntimeException wrapping the >exception. > This simple change is i believe the best change to Java in the recent >years (perhaps toes to toes with records), at least now the code of my >students does not print the stack trace and resume its course when an >exception occurs. > try { > ... > } catch(FooException e) { > throw new RuntimeException(e); > } > >Also no language presented as potential successor of Java, neither >Scala nor Kotlin have checked exceptions, because functions with >checked exceptions do not compose. >If Java wants to be the next Java, we will have to drop checked >exceptions at some point. I don't see the relevance of what other languages are doing with their own agenda's. There is no need to drop checked exceptions, they just need to be an integral part of the whole. Removing them doesn't make Java the next Java, it just makes it yet another language with slightly different syntax that succumbed to a vocal minority that values simplicity over safety. >The good news is that seeing all exceptions as unchecked exceptions is >a backward compatible change, "throws" can still be supported for >documentation purpose, the compiler can emit a warning instead of an >error if there is no catch corresponding to a checked exception and >allow everyone to catch any exceptions in the code. > > >I think we should recognize that the idea of checked exceptions was a >good idea on paper but not a good one in practice and work to retire >the concept of checked exceptions from Java. > >R?mi > >[1] >https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ > >-------------------------------------------------------------------------------- >>From: "Nathan Reynolds" >>To: "amber-dev" >>Sent: Saturday, November 12, 2022 5:47:10 AM >>Subject: Throwing Functions >>I am sorry if I am very late to the discussion. Consumer, Function, >>Predicate, and Supplier don't allow for throwing checked exceptions. >>This feature is needed in many cases. What about adding a variant >>that allows for throwing checked exceptions? For example, a >>ThrowableConsumer could be defined as such... >> >>public interface ThrowableConsumer >>{ >> void accept(T t) throws E; >>} >> >>A method that receives this as a parameter could be defined as such... >> >>public void process(ThrowableConsumer >>consume) throws E >>{ >> ... >>} >> >>The compiler takes care of ensuring the checked exception is dealt >>with in the caller to process(). >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Nov 12 16:22:14 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 12 Nov 2022 11:22:14 -0500 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: <500d565c-6281-40ba-930c-812cf4693707@oracle.com> Counter-proposal: let's just retire the endless stream of "let's just get rid of checked exceptions" proposals instead. This is all well-traveled ground, and has all been brought up before (including by you several times.)? I get how attractive it is to "just" declare checked exceptions a mistake.? But even if it was, we know that trying to "fix" the "mistakes" of the past often only makes things worse. There is interesting research going on in the Scala community and elsewhere about better treatment of effects; some of this may bear fruit that might be a better approach than the "just uncheck them all."? In any case, I'm not interested in reopening this discussion at this time; we have plenty else on the priority list. Verdict: Motion Denied.? Leave is granted to refile your motion in 2025. On 11/12/2022 2:58 AM, Remi Forax wrote: > [promoted to amber-spec-experts] > > I think we should tackle this problem at its core and retire the > notion of checked exceptions > > The written are on the wall since quite some time, but we have not yet > acknowledge it. > > Let's recap the issue and the clues pointing in that direction. > > I believe the first straw can be tracked back to the inability of the > type system to create a type variable containing the union of > exceptions when generics where introduced in Java 5. > It's the reason why there is no ThrowableConsumer, as an example, > imagine that a stream can declare a checked exception Stream and > a user call map() that takes a ThrowableFunction, > we need a way to express that the resulting stream is that combine the > exceptions of the original stream and the exceptions that may be > raised by calling the function of map. > > ? Stream? --> map(ThrowableFunction extends Exception>) -->? Stream > > Or perhaps from the beginning of Java, adding a checked exception to a > "throws" colors the function the same way the keyword async or Rust > lifetime type information color a function [1]. Adding a checked > exception to a function is not a backward compatible change. > > So we have some of idioms that beginners need to internalize to try > workaround checked exceptions, > - The oldest is i think, Thread.currentThread().interrupt() which > allows to silence an InterruptedException at the price of continuing > to execute the code until the next blocking call > - UncheckedIOException and IOError that both wrap an IOException into > an unchecked exception allowing tunneling of checked exceptions ; > wrapping an IOException and unbundle it later. > - Unsafe.rethrow (this one was retired) > - the unfamous rethrow using erasure to see a checked exception as an > unchecked exception > ?? static AssertionError rethrow(Throwable > cause) throws T { > ????? throw (T) cause; > ?? } > - IntelliJ has changed the default try/catch snippet to instead of > calling printStackTrace() to throw a RuntimeException wrapping the > exception. > ? This simple change is i believe the best change to Java in the > recent years (perhaps toes to toes with records), at least now the > code of my students does not print the stack trace and resume its > course when an exception occurs. > ??? try { > ???? ... > ??? } catch(FooException e) { > ??? ? throw new RuntimeException(e); > ??? } > > Also no language presented as potential successor of Java, neither > Scala nor Kotlin have checked exceptions, because functions with > checked exceptions do not compose. > If Java wants to be the next Java, we will have to drop checked > exceptions at some point. > > The good news is that seeing all exceptions as unchecked exceptions is > a backward compatible change, "throws" can still be supported for > documentation purpose, the compiler can emit a warning instead of an > error if there is no catch corresponding to a checked exception and > allow everyone to catch any exceptions in the code. > > I think we should recognize that the idea of checked exceptions was a > good idea on paper but not a good one in practice and work to retire > the concept of checked exceptions from Java. > > R?mi > > [1] > https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ > > ------------------------------------------------------------------------ > > *From: *"Nathan Reynolds" > *To: *"amber-dev" > *Sent: *Saturday, November 12, 2022 5:47:10 AM > *Subject: *Throwing Functions > > I am sorry if I am very late to the discussion. Consumer, > Function, Predicate, and Supplier don't allow for throwing checked > exceptions.? This feature is needed in many cases.? What about > adding a variant that allows for throwing checked exceptions?? For > example, a ThrowableConsumer could be defined as such... > > public interface ThrowableConsumer > { > ?? void accept(T t) throws E; > } > > A method that receives this as a parameter could be defined as such... > > public void process(ThrowableConsumer > consume) throws E > { > ?? ... > } > > The compiler takes care of ensuring the checked exception is dealt > with in the caller to process(). > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Nov 12 22:50:17 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 12 Nov 2022 17:50:17 -0500 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: I appreciate John's points here.? I don't necessarily agree with all of them (there's a spectrum of opinions, and that's fine, though I find #3 especially compelling), but I think this illustrates something about the exceptions debate that is easy to miss: the people who hate checked exceptions are much noisier about it than those who don't, and so they are likely to be fooled by confirmation bias since all the other loud voices seem to agree with them.? But I've found that there are a lot of people out there who quietly think checked exceptions are ... pretty OK.? Maybe even a majority.? They just don't want to engage in a big debate, they'd rather get their work done. On 11/12/2022 11:16 AM, John Hendrikx wrote: > I very much disagree with this position, so I will make a case for > checked exceptions: > > 1) Without checked exceptions, using exceptions for alternative > outcomes as part of the contract of an API would no longer be > possible. This is because adding a new exception to the API would no > longer be a breaking change. Documentation is not going to save you > here.? Effectively, exceptions would have only two uses left (like in > other languages): fatal JVM breaking errors and to signal mistakes > that could have been prevented by the programmer.? Using them to > signal unpreventable errors would be highly suspect and likely quickly > be considered a bad practice. > > In other languages (but also in some Java API's that use runtime > exception to signal unpreventable errors) the compiler cannot help you > with these alternative outcomes as it isn't even required to specify > them as part of your API. It helps you with the return value, but not > for any other outcomes. These other outcomes will now have to guessed, > gleaned from the documentation (if it is up to date), or by doing > manual code mining (of the called function and all the functions it > calls).? Worse, these outcomes can change in later releases without > any warning as they're non-breaking changes. > > 2) API's without checked exceptions will necessarily become more > cluttered than their counterparts with checked exceptions. They would > have to return wrappers or unions or special values to enforce proper > use. API design like this prevents easily deferring the handling of > rare occurrences higher up the call chain with standard idioms to keep > the current code path clean and to the point. > > 3) Removing checked exceptions now would break the **design** of many > current API's, making them dangerous to use without the checks and > balances that were present when they were designed. > > 4) Earlier design decisions to exclude checked exceptions from new > idioms and API's were a mistake that should be rectified not doubled > down upon. A large part of the momentum against checked exceptions > comes from the clumsy way that checked exceptions must be handled > while using these new features that were essentially incompatible with > existing Java.? Even with the current status quo, it is possible to > design API's that do allow checked exceptions in combination with > functional programming, it is just cumbersome to design these API's > without a dedicated union type (for exceptions).? They either support > only one checked exception or some limited number.? Their use is > however as transparent as API's that do not support checked exceptions. > > 5) The fact that the value of checked exceptions is hard to recognize > or cumbersome for beginners or non-Java developers should not have any > bearing on language design. > > Checked exceptions differentiate between the common preventable > programming errors and essential business logic. The difference > between a runtime exception (leaving out Error) and a checked > exception is that one can only occur in incorrect code, while the > other can occur even in correct code. One indicates a preventable > programming mistake and the other a missed path in your logic.? > Throwing these on the same heap will effectively reduce the usefulness > of all exceptions to programming mistakes (and fatal errors) only. > > 6) If anything, I think there should be way to promote runtime > exceptions to checked exceptions within certain modules.? This would > allow API / library designers the option to have the compiler check > their code for missed documentation and missing logic even for what > externally would be a runtime exception.? This would be especially > useful during initial design and also later refactoring as the > incredibly useful compiler checks for checked exceptions could be > extended to proper use and propagation of runtime exceptions within > that module. > > --John > > > ------ Original Message ------ > From "Remi Forax" > To "Nathan Reynolds" > Cc "amber-dev" ; "amber-spec-experts" > ; "Ron Pressler" > > Date 12/11/2022 08:58:57 > Subject Retiring Checked Exceptions Was: Throwing Functions > >> [promoted to amber-spec-experts] >> >> I think we should tackle this problem at its core and retire the >> notion of checked exceptions >> >> The written are on the wall since quite some time, but we have not >> yet acknowledge it. >> >> Let's recap the issue and the clues pointing in that direction. >> >> I believe the first straw can be tracked back to the inability of the >> type system to create a type variable containing the union of >> exceptions when generics where introduced in Java 5. >> It's the reason why there is no ThrowableConsumer, as an example, >> imagine that a stream can declare a checked exception Stream >> and a user call map() that takes a ThrowableFunction, >> we need a way to express that the resulting stream is that combine >> the exceptions of the original stream and the exceptions that may be >> raised by calling the function of map. >> >> ? Stream? --> map(ThrowableFunction> extends Exception>) -->? Stream >> >> Or perhaps from the beginning of Java, adding a checked exception to >> a "throws" colors the function the same way the keyword async or Rust >> lifetime type information color a function [1]. Adding a checked >> exception to a function is not a backward compatible change. >> >> So we have some of idioms that beginners need to internalize to try >> workaround checked exceptions, >> - The oldest is i think, Thread.currentThread().interrupt() which >> allows to silence an InterruptedException at the price of continuing >> to execute the code until the next blocking call >> - UncheckedIOException and IOError that both wrap an IOException into >> an unchecked exception allowing tunneling of checked exceptions ; >> wrapping an IOException and unbundle it later. >> - Unsafe.rethrow (this one was retired) >> - the unfamous rethrow using erasure to see a checked exception as an >> unchecked exception >> ?? static AssertionError rethrow(Throwable >> cause) throws T { >> ????? throw (T) cause; >> ?? } >> - IntelliJ has changed the default try/catch snippet to instead of >> calling printStackTrace() to throw a RuntimeException wrapping the >> exception. >> ? This simple change is i believe the best change to Java in the >> recent years (perhaps toes to toes with records), at least now the >> code of my students does not print the stack trace and resume its >> course when an exception occurs. >> ??? try { >> ???? ... >> ??? } catch(FooException e) { >> ??? ? throw new RuntimeException(e); >> ??? } >> >> Also no language presented as potential successor of Java, neither >> Scala nor Kotlin have checked exceptions, because functions with >> checked exceptions do not compose. >> If Java wants to be the next Java, we will have to drop checked >> exceptions at some point. > I don't see the relevance of what other languages are doing with their > own agenda's. There is no need to drop checked exceptions, they just > need to be an integral part of the whole. Removing them doesn't make > Java the next Java, it just makes it yet another language with > slightly different syntax that succumbed to a vocal minority that > values simplicity over safety. > >> The good news is that seeing all exceptions as unchecked exceptions >> is a backward compatible change, "throws" can still be supported for >> documentation purpose, the compiler can emit a warning instead of an >> error if there is no catch corresponding to a checked exception and >> allow everyone to catch any exceptions in the code. > >> >> >> I think we should recognize that the idea of checked exceptions was a >> good idea on paper but not a good one in practice and work to retire >> the concept of checked exceptions from Java. >> >> R?mi >> >> [1] >> https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ >> >> ------------------------------------------------------------------------ >> >> *From: *"Nathan Reynolds" >> *To: *"amber-dev" >> *Sent: *Saturday, November 12, 2022 5:47:10 AM >> *Subject: *Throwing Functions >> >> I am sorry if I am very late to the discussion. Consumer, >> Function, Predicate, and Supplier don't allow for throwing >> checked exceptions.? This feature is needed in many cases.? What >> about adding a variant that allows for throwing checked >> exceptions?? For example, a ThrowableConsumer could be defined as >> such... >> >> public interface ThrowableConsumer >> { >> ?? void accept(T t) throws E; >> } >> >> A method that receives this as a parameter could be defined as >> such... >> >> public void process(ThrowableConsumer >> consume) throws E >> { >> ?? ... >> } >> >> The compiler takes care of ensuring the checked exception is >> dealt with in the caller to process(). >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From numeralnathan at gmail.com Sat Nov 12 22:56:04 2022 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Sat, 12 Nov 2022 15:56:04 -0700 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: > I've found that there are a lot of people out there who quietly think checked exceptions are ... pretty OK. Count me in this camp. Checked exceptions are so much better than error return codes. Changing everything to unchecked exceptions would be terrible. Step 1 of programming is to write code that works for the main case. Checked exceptions ensure that some of the corner cases must be dealt with, and if handled well, the code will be more robust. For example, I wrote some code that tried to delete from the wrong part of the file system. Because I handled the IOException, the code was able to continue in the right part of the file system and the functionality wasn't broken. I can take care of the bug later instead of having to do so right away. On Sat, Nov 12, 2022 at 3:50 PM Brian Goetz wrote: > I appreciate John's points here. I don't necessarily agree with all of > them (there's a spectrum of opinions, and that's fine, though I find #3 > especially compelling), but I think this illustrates something about the > exceptions debate that is easy to miss: the people who hate checked > exceptions are much noisier about it than those who don't, and so they are > likely to be fooled by confirmation bias since all the other loud voices > seem to agree with them. But I've found that there are a lot of people out > there who quietly think checked exceptions are ... pretty OK. Maybe even a > majority. They just don't want to engage in a big debate, they'd rather > get their work done. > > > > > On 11/12/2022 11:16 AM, John Hendrikx wrote: > > I very much disagree with this position, so I will make a case for checked > exceptions: > > 1) Without checked exceptions, using exceptions for alternative outcomes > as part of the contract of an API would no longer be possible. This is > because adding a new exception to the API would no longer be a breaking > change. Documentation is not going to save you here. Effectively, > exceptions would have only two uses left (like in other languages): fatal > JVM breaking errors and to signal mistakes that could have been prevented > by the programmer. Using them to signal unpreventable errors would be > highly suspect and likely quickly be considered a bad practice. > > In other languages (but also in some Java API's that use runtime exception > to signal unpreventable errors) the compiler cannot help you with these > alternative outcomes as it isn't even required to specify them as part of > your API. It helps you with the return value, but not for any other > outcomes. These other outcomes will now have to guessed, gleaned from the > documentation (if it is up to date), or by doing manual code mining (of the > called function and all the functions it calls). Worse, these outcomes can > change in later releases without any warning as they're non-breaking > changes. > > 2) API's without checked exceptions will necessarily become more > cluttered than their counterparts with checked exceptions. They would have > to return wrappers or unions or special values to enforce proper use. API > design like this prevents easily deferring the handling of rare occurrences > higher up the call chain with standard idioms to keep the current code path > clean and to the point. > > 3) Removing checked exceptions now would break the **design** of many > current API's, making them dangerous to use without the checks and balances > that were present when they were designed. > > 4) Earlier design decisions to exclude checked exceptions from new idioms > and API's were a mistake that should be rectified not doubled down upon. A > large part of the momentum against checked exceptions comes from the clumsy > way that checked exceptions must be handled while using these new features > that were essentially incompatible with existing Java. Even with the > current status quo, it is possible to design API's that do allow checked > exceptions in combination with functional programming, it is just > cumbersome to design these API's without a dedicated union type (for > exceptions). They either support only one checked exception or some > limited number. Their use is however as transparent as API's that do not > support checked exceptions. > > 5) The fact that the value of checked exceptions is hard to recognize or > cumbersome for beginners or non-Java developers should not have any bearing > on language design. > > Checked exceptions differentiate between the common preventable > programming errors and essential business logic. The difference between a > runtime exception (leaving out Error) and a checked exception is that one > can only occur in incorrect code, while the other can occur even in correct > code. One indicates a preventable programming mistake and the other a > missed path in your logic. Throwing these on the same heap will > effectively reduce the usefulness of all exceptions to programming mistakes > (and fatal errors) only. > > 6) If anything, I think there should be way to promote runtime exceptions > to checked exceptions within certain modules. This would allow API / > library designers the option to have the compiler check their code for > missed documentation and missing logic even for what externally would be a > runtime exception. This would be especially useful during initial design > and also later refactoring as the incredibly useful compiler checks for > checked exceptions could be extended to proper use and propagation of > runtime exceptions within that module. > > --John > > > ------ Original Message ------ > From "Remi Forax" > To "Nathan Reynolds" > Cc "amber-dev" ; "amber-spec-experts" < > amber-spec-experts at openjdk.java.net>; "Ron Pressler" < > ron.pressler at oracle.com> > Date 12/11/2022 08:58:57 > Subject Retiring Checked Exceptions Was: Throwing Functions > > [promoted to amber-spec-experts] > > I think we should tackle this problem at its core and retire the notion of > checked exceptions > > The written are on the wall since quite some time, but we have not yet > acknowledge it. > > Let's recap the issue and the clues pointing in that direction. > > I believe the first straw can be tracked back to the inability of the type > system to create a type variable containing the union of exceptions when > generics where introduced in Java 5. > It's the reason why there is no ThrowableConsumer, as an example, imagine > that a stream can declare a checked exception Stream and a user call > map() that takes a ThrowableFunction, > we need a way to express that the resulting stream is that combine the > exceptions of the original stream and the exceptions that may be raised by > calling the function of map. > > Stream --> map(ThrowableFunction extends Exception>) --> Stream > > Or perhaps from the beginning of Java, adding a checked exception to a > "throws" colors the function the same way the keyword async or Rust > lifetime type information color a function [1]. Adding a checked exception > to a function is not a backward compatible change. > > So we have some of idioms that beginners need to internalize to try > workaround checked exceptions, > - The oldest is i think, Thread.currentThread().interrupt() which allows > to silence an InterruptedException at the price of continuing to execute > the code until the next blocking call > - UncheckedIOException and IOError that both wrap an IOException into an > unchecked exception allowing tunneling of checked exceptions ; wrapping an > IOException and unbundle it later. > - Unsafe.rethrow (this one was retired) > - the unfamous rethrow using erasure to see a checked exception as an > unchecked exception > static AssertionError rethrow(Throwable cause) > throws T { > throw (T) cause; > } > - IntelliJ has changed the default try/catch snippet to instead of calling > printStackTrace() to throw a RuntimeException wrapping the exception. > This simple change is i believe the best change to Java in the recent > years (perhaps toes to toes with records), at least now the code of my > students does not print the stack trace and resume its course when an > exception occurs. > try { > ... > } catch(FooException e) { > throw new RuntimeException(e); > } > > Also no language presented as potential successor of Java, neither Scala > nor Kotlin have checked exceptions, because functions with checked > exceptions do not compose. > If Java wants to be the next Java, we will have to drop checked exceptions > at some point. > > I don't see the relevance of what other languages are doing with their own > agenda's. There is no need to drop checked exceptions, they just need to be > an integral part of the whole. Removing them doesn't make Java the next > Java, it just makes it yet another language with slightly different syntax > that succumbed to a vocal minority that values simplicity over safety. > > The good news is that seeing all exceptions as unchecked exceptions is a > backward compatible change, "throws" can still be supported for > documentation purpose, the compiler can emit a warning instead of an error > if there is no catch corresponding to a checked exception and allow > everyone to catch any exceptions in the code. > > > > > I think we should recognize that the idea of checked exceptions was a good > idea on paper but not a good one in practice and work to retire the concept > of checked exceptions from Java. > > R?mi > > [1] > https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ > > ------------------------------ > > *From: *"Nathan Reynolds" > > *To: *"amber-dev" > *Sent: *Saturday, November 12, 2022 5:47:10 AM > *Subject: *Throwing Functions > > I am sorry if I am very late to the discussion. Consumer, Function, > Predicate, and Supplier don't allow for throwing checked exceptions. This > feature is needed in many cases. What about adding a variant that allows > for throwing checked exceptions? For example, a ThrowableConsumer could be > defined as such... > > public interface ThrowableConsumer > { > void accept(T t) throws E; > } > > A method that receives this as a parameter could be defined as such... > > public void process(ThrowableConsumer consume) > throws E > { > ... > } > > The compiler takes care of ensuring the checked exception is dealt with in > the caller to process(). > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Sun Nov 13 17:48:55 2022 From: redio.development at gmail.com (redio.development at gmail.com) Date: Sun, 13 Nov 2022 18:48:55 +0100 Subject: switch exhaustiveness check not working within deconstruction pattern. Message-ID: <004401d8f788$337b2130$9a716390$@gmail.com> package dev.redio; import static dev.redio.Main.Activity.*; public class Main { public static void main(String[] args) { record Pair(boolean weekend, Activity activity) {} var pair = new Pair(true, new Sleeping(8)); var msg = switch (pair) { // <- A switch expression should have a default case Java(1073743531) case Pair(boolean b, Sleeping s) -> "3"; case Pair(boolean b, Skiing s) -> "4"; case Pair(boolean b, Coding c) -> "5"; }; System.out.println(msg); } sealed interface Activity { record Sleeping(int hours) implements Activity {} record Skiing(String resort) implements Activity {} record Coding() implements Activity {} } } This deconstruction should be exhaustive since Pair is a guaranteed match boolean doesn't have subclasses and Activity is sealed and fully covered. Null should throw in this case. A default case would never be reached. (Compiling and running with (dead) default case) -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Nov 13 18:54:31 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 13 Nov 2022 13:54:31 -0500 Subject: switch exhaustiveness check not working within deconstruction pattern. Message-ID: I just tested this locally, and this works on the latest version of Java 19. Compiles successfully. Can you confirm that that is the version that you are using? Running the following command should print the following output (or something similar). $ java --version openjdk 19 2022-09-20 OpenJDK Runtime Environment (build 19+36-2238) OpenJDK 64-Bit Server VM (build 19+36-2238, mixed mode, sharing) $ javac Main.java --enable-preview --release 19 Note: Main.java uses preview features of Java SE 19. Note: Recompile with -Xlint:preview for details. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Nov 13 18:59:03 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 13 Nov 2022 13:59:03 -0500 Subject: switch exhaustiveness check not working within deconstruction pattern. Message-ID: I should also add, it spit out 3 as the answer. public class Main { public static sealed interface Activity {} public static record Sleeping(int hours) implements Activity {} public static record Skiing(String resort) implements Activity {} public static record Coding() implements Activity {} public static void main(String[] args) { record Pair(boolean weekend, Activity activity) {} var pair = new Pair(true, new Sleeping(8)); var msg = switch (pair) { case Pair(boolean b, Sleeping s) -> "3"; case Pair(boolean b, Skiing s) -> "4"; case Pair(boolean b, Coding c) -> "5"; }; System.out.println(msg); } } -------------- next part -------------- An HTML attachment was scrubbed... URL: From jan.lahoda at oracle.com Mon Nov 14 11:41:28 2022 From: jan.lahoda at oracle.com (Jan Lahoda) Date: Mon, 14 Nov 2022 11:41:28 +0000 Subject: switch exhaustiveness check not working within deconstruction pattern. In-Reply-To: <004401d8f788$337b2130$9a716390$@gmail.com> References: <004401d8f788$337b2130$9a716390$@gmail.com> Message-ID: Hi, This should indeed be exhaustive, AFAIK, and javac from JDK 19 seems to be happy with the code: --- $ ./javac --enable-preview -source 19 /tmp/Main.java Note: /tmp/Main.java uses preview features of Java SE 19. Note: Recompile with -Xlint:preview for details. --- What are the precise steps to reproduce/JDK version? Thanks, Jan ________________________________ From: amber-dev on behalf of redio.development at gmail.com Sent: Sunday, November 13, 2022 6:48 PM To: amber-dev at openjdk.org Subject: switch exhaustiveness check not working within deconstruction pattern. package dev.redio; import static dev.redio.Main.Activity.*; public class Main { public static void main(String[] args) { record Pair(boolean weekend, Activity activity) {} var pair = new Pair(true, new Sleeping(8)); var msg = switch (pair) { // <- A switch expression should have a default case Java(1073743531) case Pair(boolean b, Sleeping s) -> "3"; case Pair(boolean b, Skiing s) -> "4"; case Pair(boolean b, Coding c) -> "5"; }; System.out.println(msg); } sealed interface Activity { record Sleeping(int hours) implements Activity {} record Skiing(String resort) implements Activity {} record Coding() implements Activity {} } } This deconstruction should be exhaustive since Pair is a guaranteed match boolean doesn?t have subclasses and Activity is sealed and fully covered. Null should throw in this case. A default case would never be reached. (Compiling and running with (dead) default case) -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Mon Nov 14 16:33:35 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 14 Nov 2022 10:33:35 -0600 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: On Sat, Nov 12, 2022 at 5:26 PM Nathan Reynolds wrote: > > I've found that there are a lot of people out there who quietly think > checked exceptions are ... pretty OK. > > Count me in this camp. Checked exceptions are so much better than error > return codes. > I like checked exceptions also. But so what? Of course everyone including me is going to have some opinion... the deeper question is, what are we trying to optimize for in a programming language? Well I'm sure everyone has an opinion on that question as well.. but at least that question is a little closer to the heart of the matter. A problem I've seen with programming language design happens in other fields as well. Consider building architecture for example. Architects can sometimes drift from optimizing for the humans that will occupy the space (what should be their goal) to optimizing for simplicity, beauty, and elegance (what they really want to be their goal). Simplicity, beauty, and elegance are all well and good until they start detracting from the more important goal of serving the humans. Have you ever had dinner in a nice restaurant that has sleek & beautiful architecture, but because of all the open space it's really drafty and because of the hard concrete ceilings and resulting terrible acoustics you can barely understand what anyone across the table is saying? (yes, that just happened a few weeks ago) So here's my opinion on what we should be optimizing for, from my own little perspective. I'm not a language designer (those folks are a lot smarter than me :) I'm just a regular Java user for 20+ years. My main development responsibility right now is an enterprise web application with ~150,000 lines of code, plus millions of LOC incorporated by reference (Spring, Hibernate, Tomcat, Linux, etc.). But the windows on my screen are only ~54 lines tall. That means I can inspect at most 0.03% of the code at any one time, and yet I'm supposed to somehow ensure this whole thing works properly without any bugs. Therefore, to me the most important property of a programming language is this: how hard is it to look at a chunk of code on the screen and be able to convince myself that it will do what it's supposed to and not contribute any bugs? Is most of the critical information there in front of me? Can I convince myself I'm looking at a seaworthy vessel of logical correctness? Or are there a bunch of "leaks" that I either have to patch, or (almost worse) do a bunch of research to finally realize that in fact they really aren't leaks after all? How hard is it to hunt down the information that's not explicitly on the screen that I need to feel confident in what I'm looking at? No doubt Java would be more simple, beautiful, and elegant without checked exceptions. But checked exceptions are explicit information on the screen relating to what error cases can possibly occur. They appear pretty much when and where you need to see them. And the compiler guarantees that you won't accidentally miss one. >From the perspective of "Help me prove this code is correct and handles all the possible error cases" what's not to love about that?? Features that help my cause: - Compile time explicit strong typing - Precisely defined semantics (compare to C: order of execution, memory model, etc.) - Easily accessible Javadocs that specify behavior precisely - Compiler warnings - @Overrides - Checked exceptions Features that don't help my cause (they're not bad, they're just less important): - Anything that optimizes for the speed of writing code rather than the speed of reading it - Anything that optimizes for code brevity (if that were the goal, we'd all be using perl!) An example of the latter is the new "var" keyword. Yes it makes it faster to WRITE code, but it makes it a tiny bit slower to READ code. It means to understand what "foo" is, I have to do one little tiny extra step of type inference in my head. Not a big deal, and I'm certainly not opposed to the feature, but I probably won't use it much. Java's "target market" as it were is large enterprise projects. Regarding such projects, a wise person once said: *You write code once, but you maintain it forever.* -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From numeralnathan at gmail.com Mon Nov 14 16:39:51 2022 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Mon, 14 Nov 2022 09:39:51 -0700 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: These are great points! Can we start a Wiki page and get all the points for and against checked exceptions? Then when others come along, we can point them to the page and they can add to the discussion instead of rehashing it. On Mon, Nov 14, 2022 at 9:33 AM Archie Cobbs wrote: > On Sat, Nov 12, 2022 at 5:26 PM Nathan Reynolds > wrote: > >> > I've found that there are a lot of people out there who quietly think >> checked exceptions are ... pretty OK. >> >> Count me in this camp. Checked exceptions are so much better than error >> return codes. >> > > I like checked exceptions also. > > But so what? Of course everyone including me is going to have some > opinion... the deeper question is, what are we trying to optimize for in a > programming language? > > Well I'm sure everyone has an opinion on that question as well.. but at > least that question is a little closer to the heart of the matter. > > A problem I've seen with programming language design happens in other > fields as well. Consider building architecture for example. Architects can > sometimes drift from optimizing for the humans that will occupy the space > (what should be their goal) to optimizing for simplicity, beauty, and > elegance (what they really want to be their goal). > > Simplicity, beauty, and elegance are all well and good until they start > detracting from the more important goal of serving the humans. Have you > ever had dinner in a nice restaurant that has sleek & beautiful > architecture, but because of all the open space it's really drafty and > because of the hard concrete ceilings and resulting terrible acoustics you > can barely understand what anyone across the table is saying? (yes, that > just happened a few weeks ago) > > So here's my opinion on what we should be optimizing for, from my own > little perspective. > > I'm not a language designer (those folks are a lot smarter than me :) I'm > just a regular Java user for 20+ years. My main development responsibility > right now is an enterprise web application with ~150,000 lines of code, > plus millions of LOC incorporated by reference (Spring, Hibernate, Tomcat, > Linux, etc.). > > But the windows on my screen are only ~54 lines tall. > > That means I can inspect at most 0.03% of the code at any one time, and > yet I'm supposed to somehow ensure this whole thing works properly without > any bugs. > > Therefore, to me the most important property of a programming language is > this: how hard is it to look at a chunk of code on the screen and be able > to convince myself that it will do what it's supposed to and not contribute > any bugs? > > Is most of the critical information there in front of me? Can I convince > myself I'm looking at a seaworthy vessel of logical correctness? > > Or are there a bunch of "leaks" that I either have to patch, or (almost > worse) do a bunch of research to finally realize that in fact they really > aren't leaks after all? > > How hard is it to hunt down the information that's not explicitly on the > screen that I need to feel confident in what I'm looking at? > > No doubt Java would be more simple, beautiful, and elegant without checked > exceptions. But checked exceptions are explicit information on the screen > relating to what error cases can possibly occur. They appear pretty much > when and where you need to see them. And the compiler guarantees that you > won't accidentally miss one. > > From the perspective of "Help me prove this code is correct and handles > all the possible error cases" what's not to love about that?? > > Features that help my cause: > - Compile time explicit strong typing > - Precisely defined semantics (compare to C: order of execution, memory > model, etc.) > - Easily accessible Javadocs that specify behavior precisely > - Compiler warnings > - @Overrides > - Checked exceptions > > Features that don't help my cause (they're not bad, they're just less > important): > - Anything that optimizes for the speed of writing code rather than the > speed of reading it > - Anything that optimizes for code brevity (if that were the goal, we'd > all be using perl!) > > An example of the latter is the new "var" keyword. Yes it makes it faster > to WRITE code, but it makes it a tiny bit slower to READ code. It means to > understand what "foo" is, I have to do one little tiny extra step of type > inference in my head. Not a big deal, and I'm certainly not opposed to the > feature, but I probably won't use it much. > > Java's "target market" as it were is large enterprise projects. Regarding > such projects, a wise person once said: *You write code once, but you > maintain it forever.* > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Nov 14 17:54:55 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Nov 2022 12:54:55 -0500 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: On 11/14/2022 11:33 AM, Archie Cobbs wrote: > > I like checked exceptions also. I think the replies here have proven my point. It's an easy opinion to say "checked exceptions were a failed experiment" -- likely too-easy an opinion -- but people have a right to their opinions. But even if one conceded this point, that doesn't mean "so back it out" is a smart move.? People have built up billions of lines of code based on assumptions about how exceptions work; pulling the rug out from under them has a real and pervasive cost.? Banging the "failed experiment" drum incorrectly assumes those costs away. But more importantly, the "failed experiment" crowd seems to take it as an article of faith that everyone agrees with them, because there are not protest rallies in the street chanting "Save Checked Exceptions".? But it did not take long to elicit a few well-reasoned "I prefer the status quo" opinions.? There are plenty of people who think the status quo is fine, and that a radical change would be worse, they're just not making a big deal about it, instead they're getting work done. Yes, it is unfortunate that some early JDK APIs used checked exceptions poorly, before we really understood what they were good and bad for.? And yes, its unfortunate that they have inconvenient interactions with other language features, such as lambdas. Another aspect of the unhelpfulness of the "let's just kill them" argument is that Nathan's modest request gets lost in the shuffle. Nathan came with a reasonable request -- "can we have a few more functional interfaces" -- and instead, someone used it as an excuse to start a holy war and reasonable mitigations easily get lost along the way. -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Mon Nov 14 20:42:00 2022 From: redio.development at gmail.com (redio.development at gmail.com) Date: Mon, 14 Nov 2022 21:42:00 +0100 Subject: AW: switch exhaustiveness check not working within deconstruction pattern. In-Reply-To: References: <004401d8f788$337b2130$9a716390$@gmail.com> Message-ID: <002e01d8f869$8c1e9a60$a45bcf20$@gmail.com> I use VSCode with the "Extension Pack for Java". I back checked on my (still linting) code using javac directly and it's in fact not an error there. I assumed the (error) linting of the VSCode extension was based on javac output. But I guess it's a bug in their own code checking system unrelated to javac output. I will open an issue on the extension instead. Sorry to bother you and take your time for this. Great regards RedIODev Von: Jan Lahoda Gesendet: Montag, 14. November 2022 12:41 An: redio.development at gmail.com; amber-dev at openjdk.org Betreff: Re: switch exhaustiveness check not working within deconstruction pattern. Hi, This should indeed be exhaustive, AFAIK, and javac from JDK 19 seems to be happy with the code: --- $ ./javac --enable-preview -source 19 /tmp/Main.java Note: /tmp/Main.java uses preview features of Java SE 19. Note: Recompile with -Xlint:preview for details. --- What are the precise steps to reproduce/JDK version? Thanks, Jan _____ From: amber-dev > on behalf of redio.development at gmail.com > Sent: Sunday, November 13, 2022 6:48 PM To: amber-dev at openjdk.org > Subject: switch exhaustiveness check not working within deconstruction pattern. package dev.redio; import static dev.redio.Main.Activity.*; public class Main { public static void main(String[] args) { record Pair(boolean weekend, Activity activity) {} var pair = new Pair(true, new Sleeping(8)); var msg = switch (pair) { // <- A switch expression should have a default case Java(1073743531) case Pair(boolean b, Sleeping s) -> "3"; case Pair(boolean b, Skiing s) -> "4"; case Pair(boolean b, Coding c) -> "5"; }; System.out.println(msg); } sealed interface Activity { record Sleeping(int hours) implements Activity {} record Skiing(String resort) implements Activity {} record Coding() implements Activity {} } } This deconstruction should be exhaustive since Pair is a guaranteed match boolean doesn't have subclasses and Activity is sealed and fully covered. Null should throw in this case. A default case would never be reached. (Compiling and running with (dead) default case) -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Nov 14 22:52:06 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 14 Nov 2022 23:52:06 +0100 (CET) Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: <1142617653.45463508.1668466326897.JavaMail.zimbra@u-pem.fr> > From: "John Hendrikx" > To: "Remi Forax" , "Nathan Reynolds" > > Cc: "amber-dev" , "amber-spec-experts" > , "Ron Pressler" > Sent: Saturday, November 12, 2022 5:16:10 PM > Subject: Re: Retiring Checked Exceptions Was: Throwing Functions > I very much disagree with this position, so I will make a case for checked > exceptions: > 1) Without checked exceptions, using exceptions for alternative outcomes as part > of the contract of an API would no longer be possible. This is because adding a > new exception to the API would no longer be a breaking change. Documentation is > not going to save you here. Effectively, exceptions would have only two uses > left (like in other languages): fatal JVM breaking errors and to signal > mistakes that could have been prevented by the programmer. Using them to signal > unpreventable errors would be highly suspect and likely quickly be considered a > bad practice. > In other languages (but also in some Java API's that use runtime exception to > signal unpreventable errors) the compiler cannot help you with these > alternative outcomes as it isn't even required to specify them as part of your > API. It helps you with the return value, but not for any other outcomes. These > other outcomes will now have to guessed, gleaned from the documentation (if it > is up to date), or by doing manual code mining (of the called function and all > the functions it calls). Worse, these outcomes can change in later releases > without any warning as they're non-breaking changes. I'm proposing to demote checked exceptions to make them second class citizen because this practically what they are. The problem is that the status quo is pushing people to use runtime exceptions instead of exceptions because checked exception do not compose well, exactly what we both do not want. Are you aware that Files.lines() or Files.list() are throwing UncheckedIOException ? Libraries are already hiding checked exceptions inside unchecked ones. The end game if we do nothing is that nobody will use Exception anymore because they will be hidden inside an unchecked one. I think it's far better to consider them as documentation with a warning, exacly like a a cast to a type variable or a parametrized type emit a warning that that current practice which is to hide checked exceptions inside unchecked ones. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Nov 14 22:58:29 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 14 Nov 2022 23:58:29 +0100 (CET) Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> Message-ID: <107377019.45464042.1668466709989.JavaMail.zimbra@u-pem.fr> > From: "Archie Cobbs" > To: "Nathan Reynolds" > Cc: "Brian Goetz" , "John Hendrikx" , > "Remi Forax" , "amber-dev" , > "amber-spec-experts" , "Ron Pressler" > > Sent: Monday, November 14, 2022 5:33:35 PM > Subject: Re: Retiring Checked Exceptions Was: Throwing Functions > On Sat, Nov 12, 2022 at 5:26 PM Nathan Reynolds < [ > mailto:numeralnathan at gmail.com | numeralnathan at gmail.com ] > wrote: >>> I've found that there are a lot of people out there who quietly think checked >> > exceptions are ... pretty OK. >> Count me in this camp. Checked exceptions are so much better than error return >> codes. > I like checked exceptions also. > But so what? Of course everyone including me is going to have some opinion... > the deeper question is, what are we trying to optimize for in a programming > language? > Well I'm sure everyone has an opinion on that question as well.. but at least > that question is a little closer to the heart of the matter. > A problem I've seen with programming language design happens in other fields as > well. Consider building architecture for example. Architects can sometimes > drift from optimizing for the humans that will occupy the space (what should be > their goal) to optimizing for simplicity, beauty, and elegance (what they > really want to be their goal). > Simplicity, beauty, and elegance are all well and good until they start > detracting from the more important goal of serving the humans. Have you ever > had dinner in a nice restaurant that has sleek & beautiful architecture, but > because of all the open space it's really drafty and because of the hard > concrete ceilings and resulting terrible acoustics you can barely understand > what anyone across the table is saying? (yes, that just happened a few weeks > ago) > So here's my opinion on what we should be optimizing for, from my own little > perspective. > I'm not a language designer (those folks are a lot smarter than me :) I'm just a > regular Java user for 20+ years. My main development responsibility right now > is an enterprise web application with ~150,000 lines of code, plus millions of > LOC incorporated by reference (Spring, Hibernate, Tomcat, Linux, etc.). > But the windows on my screen are only ~54 lines tall. > That means I can inspect at most 0.03% of the code at any one time, and yet I'm > supposed to somehow ensure this whole thing works properly without any bugs. > Therefore, to me the most important property of a programming language is this: > how hard is it to look at a chunk of code on the screen and be able to convince > myself that it will do what it's supposed to and not contribute any bugs? > Is most of the critical information there in front of me? Can I convince myself > I'm looking at a seaworthy vessel of logical correctness? > Or are there a bunch of "leaks" that I either have to patch, or (almost worse) > do a bunch of research to finally realize that in fact they really aren't leaks > after all? > How hard is it to hunt down the information that's not explicitly on the screen > that I need to feel confident in what I'm looking at? > No doubt Java would be more simple, beautiful, and elegant without checked > exceptions. But checked exceptions are explicit information on the screen > relating to what error cases can possibly occur. They appear pretty much when > and where you need to see them. And the compiler guarantees that you won't > accidentally miss one. > From the perspective of "Help me prove this code is correct and handles all the > possible error cases" what's not to love about that?? > Features that help my cause: > - Compile time explicit strong typing > - Precisely defined semantics (compare to C: order of execution, memory model, > etc.) > - Easily accessible Javadocs that specify behavior precisely > - Compiler warnings > - @Overrides > - Checked exceptions > Features that don't help my cause (they're not bad, they're just less > important): > - Anything that optimizes for the speed of writing code rather than the speed of > reading it > - Anything that optimizes for code brevity (if that were the goal, we'd all be > using perl!) > An example of the latter is the new "var" keyword. Yes it makes it faster to > WRITE code, but it makes it a tiny bit slower to READ code. It means to > understand what "foo" is, I have to do one little tiny extra step of type > inference in my head. Not a big deal, and I'm certainly not opposed to the > feature, but I probably won't use it much. > Java's "target market" as it were is large enterprise projects. Regarding such > projects, a wise person once said: You write code once, but you maintain it > forever. What if checked exceptions work like unchecked casts ? What if instead of a compiler error you still have a warning when you suppress a checked exception, so you can write code like this public void m() throws IOException { var input = ... Runnable runnable = () -> { input.read(); }; runnable.run() ; } and you get two warnings, one because you have suppressed the IOException inside the Runnable and one because you have a throws IOException with no IOException thrown inside the body of m() ? So IOException will work seamlessly with streams, methods that takes lambdas, etc. > -Archie > -- > Archie L. Cobbs R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Mon Nov 14 23:09:03 2022 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 14 Nov 2022 17:09:03 -0600 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: <1142617653.45463508.1668466326897.JavaMail.zimbra@u-pem.fr> References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> <1142617653.45463508.1668466326897.JavaMail.zimbra@u-pem.fr> Message-ID: On Mon, Nov 14, 2022 at 4:52 PM wrote: > I'm proposing to demote checked exceptions to make them second class > citizen because this practically what they are. > The problem is that the status quo is pushing people to use runtime > exceptions instead of exceptions because checked exception do not compose > well, exactly what we both do not want. > I totally agree that the lack of composibility (composibleness?) is a problem. But I don't agree that ALL of the blame for that problem rests on checked exceptions. After all, checked exceptions have been around a lot longer than lambdas. > What if checked exceptions work like unchecked casts ? > This sounds like a much more promising direction to go in. All we want is a simple way to tell the compiler "I know this lambda can throw a checked exception, just pass it through instead of making this impossible to compile". E.g. something like this?? public void execute(@PassThroughCheckedExceptions Supplier getter) { return getter.get(); } public InputStream openFile(File file) throws IOException { this.execute(FileInputStream::new); } -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Nov 14 23:15:49 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 14 Nov 2022 18:15:49 -0500 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: <107377019.45464042.1668466709989.JavaMail.zimbra@u-pem.fr> References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> <107377019.45464042.1668466709989.JavaMail.zimbra@u-pem.fr> Message-ID: A brief reminder: this is getting out of the charter of amber-dev. On 11/14/2022 5:58 PM, forax at univ-mlv.fr wrote: > > > ------------------------------------------------------------------------ > > *From: *"Archie Cobbs" > *To: *"Nathan Reynolds" > *Cc: *"Brian Goetz" , "John Hendrikx" > , "Remi Forax" , "amber-dev" > , "amber-spec-experts" > , "Ron Pressler" > > *Sent: *Monday, November 14, 2022 5:33:35 PM > *Subject: *Re: Retiring Checked Exceptions Was: Throwing Functions > > On Sat, Nov 12, 2022 at 5:26 PM Nathan Reynolds > wrote: > > > I've found that there are a lot of people out there who > quietly think checked exceptions are ... pretty OK. > > Count me in this camp.? Checked exceptions are so much better > than error return codes. > > > I like checked exceptions also. > > But so what? Of course everyone including me is going to have some > opinion... the deeper question is, what are we trying to optimize > for in a programming language? > > Well I'm sure everyone has an opinion on that question as well.. > but at least that question is a little closer to the heart of the > matter. > > A problem I've seen with programming language design happens in > other fields as well. Consider building architecture for example. > Architects can sometimes drift from optimizing for the humans that > will occupy the space (what should be their goal) to optimizing > for simplicity, beauty, and elegance (what they really want to be > their goal). > > Simplicity, beauty, and elegance are all well and good until they > start detracting from the more important goal of serving the > humans. Have you ever had dinner in a nice restaurant that has > sleek & beautiful architecture, but because of all the open space > it's really drafty and because of the hard concrete ceilings and > resulting terrible acoustics you can barely understand what anyone > across the table is saying? (yes, that just happened a few weeks ago) > > So here's my opinion on what we should be optimizing for, from my > own little perspective. > > I'm not a language designer (those folks are a lot smarter than me > :) I'm just a regular Java user for 20+ years. My main development > responsibility right now is an enterprise web application with > ~150,000 lines of code, plus millions of LOC incorporated by > reference (Spring, Hibernate, Tomcat, Linux, etc.). > > But the windows on my screen are only ~54 lines tall. > > That means I can inspect at most 0.03% of the code at any one > time, and yet I'm supposed to somehow ensure this whole thing > works properly without any bugs. > > Therefore, to me the most important property of a programming > language is this: how hard is it to look at a chunk of code on the > screen and be able to convince myself that it will do what it's > supposed to and not contribute any bugs? > > Is most of the critical information there in front of me? Can I > convince myself I'm looking at a seaworthy vessel of logical > correctness? > > Or are there a bunch of "leaks" that I either have to patch, or > (almost worse) do a bunch of research to finally realize that in > fact they really aren't leaks after all? > > How hard is it to hunt down the information that's not explicitly > on the screen that I need to feel confident in what I'm looking at? > > No doubt Java would be more simple, beautiful, and elegant without > checked exceptions. But checked exceptions are explicit > information on the screen relating to what error cases can > possibly occur. They appear pretty much when and where you need to > see them. And the compiler guarantees that you won't accidentally > miss one. > > From the perspective of "Help me prove this code is correct and > handles all the possible error cases" what's not to love about that?? > > Features that help my cause: > ? - Compile time explicit strong typing > ? - Precisely defined semantics (compare to C: order of execution, > memory model, etc.) > ? - Easily accessible Javadocs that specify behavior precisely > ? - Compiler warnings > ? - @Overrides > ? - Checked exceptions > > Features that don't help my cause (they're not bad, they're just > less important): > ? - Anything that optimizes for the speed of writing code rather > than the speed of reading it > ? - Anything that optimizes for code brevity (if that were the > goal, we'd all be using perl!) > > An example of the latter is the new "var" keyword. Yes it makes it > faster to WRITE code, but it makes it a tiny bit slower to READ > code. It means to understand what "foo" is, I have to do one > little tiny extra step of type inference in my head. Not a big > deal, and I'm certainly not opposed to the feature, but I probably > won't use it much. > > Java's "target market" as it were is large enterprise projects. > Regarding such projects, a wise person once said: /You write code > once, but you maintain it forever./ > > > What if checked exceptions work like unchecked casts ? > > What if instead of a compiler error you still have a warning when you > suppress a checked exception, so you can write code like this > > ? public void m() throws IOException { > ??? var input = ... > ??? Runnable runnable = () -> { > ?????? input.read(); > ??? }; > ??? runnable.run() > ;? } > > and you get two warnings, one because you have suppressed the > IOException inside the Runnable and one because you have a throws > IOException with no IOException thrown inside the body of m() ? > > So IOException will work seamlessly with streams, methods that takes > lambdas, etc. > > > > -Archie > > -- > Archie L. Cobbs > > > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From scolebourne at joda.org Tue Nov 15 09:41:15 2022 From: scolebourne at joda.org (Stephen Colebourne) Date: Tue, 15 Nov 2022 09:41:15 +0000 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> <1142617653.45463508.1668466326897.JavaMail.zimbra@u-pem.fr> Message-ID: I outlined simple, practical approach to this when lambdas were first added to Java [1]. Much of the blog post above is no longer especially interesting, but the basic "lone throws" concept still is. The key part is repeated in these three bullet points: * Any method may have a throws keyword without specifying the types that are thrown ("lone-throws"). This indicates that any exception, checked or unchecked may be thrown. Once thrown in this manner, any checked exception flows up the stack in an unchecked manner. * Any catch clause may have a throws keyword after the catch. This indicates that any exception may be caught, even if the exception isn't known to be thrown by the try block. * All closures are implicitly declared with lone throws. Thus, all closures can throw checked and unchecked exceptions without declaring the checked ones. I suspect it is too late for the last bullet point to be adopted, but the first two would IMO still be hugely beneficial to Java. The point here is not to reject the idea of checked exceptions, but to provide a convenient way to convert/handle them when they are not what you want. Various alternative mechanisms are in use today: * additional functional interfaces such as ThrowableFunction * utilities like Unchecked.wrap(ThrowableSupplier) which convert checked to unchecked * hacks like "sneaky throw" My argument is that there is a very clear use case in the global corpus of Java code for a mechanism to convert/handle checked exceptions more like unchecked. And that this would be a good language feature, given that it can be done without threatening those who appreciate checked exceptions. Stephen [1] https://blog.joda.org/2010/06/exception-transparency-and-lone-throws_9915.html?m=1 On Mon, 14 Nov 2022, 23:09 Archie Cobbs, wrote: > On Mon, Nov 14, 2022 at 4:52 PM wrote: > >> I'm proposing to demote checked exceptions to make them second class >> citizen because this practically what they are. >> The problem is that the status quo is pushing people to use runtime >> exceptions instead of exceptions because checked exception do not compose >> well, exactly what we both do not want. >> > > I totally agree that the lack of composibility (composibleness?) is a > problem. But I don't agree that ALL of the blame for that problem rests on > checked exceptions. After all, checked exceptions have been around a lot > longer than lambdas. > > >> What if checked exceptions work like unchecked casts ? >> > > This sounds like a much more promising direction to go in. > > All we want is a simple way to tell the compiler "I know this lambda can > throw a checked exception, just pass it through instead of making this > impossible to compile". > > E.g. something like this?? > > public void execute(@PassThroughCheckedExceptions Supplier getter) > { > return getter.get(); > } > > public InputStream openFile(File file) throws IOException { > this.execute(FileInputStream::new); > } > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From hjohn at xs4all.nl Tue Nov 15 12:15:27 2022 From: hjohn at xs4all.nl (John Hendrikx) Date: Tue, 15 Nov 2022 12:15:27 +0000 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> <1142617653.45463508.1668466326897.JavaMail.zimbra@u-pem.fr> Message-ID: I just wanted to add an observation here. I now have seen several ideas here that seem to point to a solution which just allows lambda's to throw checked exceptions (with a warning or automatically converted to unchecked), but what I find odd is that this wouldn't solve anything at all. These functions are almost by definition not used or called immediately, and so the actual checked exception can occur in a totally unrelated part of the code that may not be prepared to handle it: this unrelated code would not be declaring any exceptions and the compiler would give no warnings or errors, but a checked exception can now pop up there uninvited. The simplest example are streams. When you write: Stream createStream() { return List.of("file:/xyz").stream().map(URI::new); // throws URISyntaxException } There is no need to declare anything, and any warnings or errors would make no sense. Allowing the above method to declare "URISyntaxException" is just plain incorrect, leaving you to write a catch block (or rethrow) to deal with something that can't happen. Then in a completely unrelated part of the code you may use this stream: stream.toList(); There is no sign of the lambda, toList() doesn't declare checked exceptions, but this can now throw URISyntaxException. IMHO solutions that just allow you to pretend the checked exception doesn't exist are going to just muddy the waters further, and don't really solve the problem at all. They just make it easier to hide the problem, and in doing so, create new problems. I think it is possible to do better and find a solution that doesn't come with a new set of problems. --John ------ Original Message ------ >From "Stephen Colebourne" To "Amber dev" Date 15/11/2022 10:41:15 Subject Re: Retiring Checked Exceptions Was: Throwing Functions >I outlined simple, practical approach to this when lambdas were first >added to Java [1]. Much of the blog post above is no longer especially >interesting, but the basic "lone throws" concept still is. The key part >is repeated in these three bullet points: > >* Any method may have a throws keyword without specifying the types >that are thrown ("lone-throws"). This indicates that any exception, >checked or unchecked may be thrown. Once thrown in this manner, any >checked exception flows up the stack in an unchecked manner. >* Any catch clause may have a throws keyword after the catch. This >indicates that any exception may be caught, even if the exception isn't >known to be thrown by the try block. >* All closures are implicitly declared with lone throws. Thus, all >closures can throw checked and unchecked exceptions without declaring >the checked ones. > > >I suspect it is too late for the last bullet point to be adopted, but >the first two would IMO still be hugely beneficial to Java. The point >here is not to reject the idea of checked exceptions, but to provide a >convenient way to convert/handle them when they are not what you want. > >Various alternative mechanisms are in use today: >* additional functional interfaces such as ThrowableFunction >* utilities like Unchecked.wrap(ThrowableSupplier) which convert >checked to unchecked >* hacks like "sneaky throw" >My argument is that there is a very clear use case in the global corpus >of Java code for a mechanism to convert/handle checked exceptions more >like unchecked. And that this would be a good language feature, given >that it can be done without threatening those who appreciate checked >exceptions. > >Stephen > > >[1] >https://blog.joda.org/2010/06/exception-transparency-and-lone-throws_9915.html?m=1 > >On Mon, 14 Nov 2022, 23:09 Archie Cobbs, >wrote: >>On Mon, Nov 14, 2022 at 4:52 PM wrote: >>>I'm proposing to demote checked exceptions to make them second class >>>citizen because this practically what they are. >>>The problem is that the status quo is pushing people to use runtime >>>exceptions instead of exceptions because checked exception do not >>>compose well, exactly what we both do not want. >> >>I totally agree that the lack of composibility (composibleness?) is a >>problem. But I don't agree that ALL of the blame for that problem >>rests on checked exceptions. After all, checked exceptions have been >>around a lot longer than lambdas. >> >>>What if checked exceptions work like unchecked casts ? >> >>This sounds like a much more promising direction to go in. >> >>All we want is a simple way to tell the compiler "I know this lambda >>can throw a checked exception, just pass it through instead of making >>this impossible to compile". >> >>E.g. something like this?? >> >>public void execute(@PassThroughCheckedExceptions Supplier >>getter) { >> return getter.get(); >>} >> >>public InputStream openFile(File file) throws IOException { >> this.execute(FileInputStream::new); >>} >> >>-Archie >> >>-- >>Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Nov 15 15:44:55 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 15 Nov 2022 16:44:55 +0100 (CET) Subject: AW: switch exhaustiveness check not working within deconstruction pattern. In-Reply-To: <002e01d8f869$8c1e9a60$a45bcf20$@gmail.com> References: <004401d8f788$337b2130$9a716390$@gmail.com> <002e01d8f869$8c1e9a60$a45bcf20$@gmail.com> Message-ID: <2052952063.46176254.1668527095313.JavaMail.zimbra@u-pem.fr> > From: "Red IO" > To: "jan lahoda" , "amber-dev" > Sent: Monday, November 14, 2022 9:42:00 PM > Subject: AW: switch exhaustiveness check not working within deconstruction > pattern. > I use VSCode with the ?Extension Pack for Java?. > I back checked on my (still linting) code using javac directly and it?s in fact > not an error there. > I assumed the (error) linting of the VSCode extension was based on javac output. > But I guess it?s a bug in their own code checking system unrelated to javac > output. > I will open an issue on the extension instead. > Sorry to bother you and take your time for this. The linting in VSCode is based on ecj, the eclipse compiler so it seems there is a bug in ecj. > Great regards > RedIODev regards, R?mi > Von: Jan Lahoda > Gesendet: Montag, 14. November 2022 12:41 > An: redio.development at gmail.com; amber-dev at openjdk.org > Betreff: Re: switch exhaustiveness check not working within deconstruction > pattern. > Hi, > This should indeed be exhaustive, AFAIK, and javac from JDK 19 seems to be happy > with the code: > --- > $ ./javac --enable-preview -source 19 /tmp/Main.java > Note: /tmp/Main.java uses preview features of Java SE 19. > Note: Recompile with -Xlint:preview for details. > --- > What are the precise steps to reproduce/JDK version? > Thanks, > Jan > From: amber-dev < [ mailto:amber-dev-retn at openjdk.org | > amber-dev-retn at openjdk.org ] > on behalf of [ > mailto:redio.development at gmail.com | redio.development at gmail.com ] < [ > mailto:redio.development at gmail.com | redio.development at gmail.com ] > > Sent: Sunday, November 13, 2022 6:48 PM > To: [ mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] < [ > mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] > > Subject: switch exhaustiveness check not working within deconstruction pattern. > package dev . redio ; > import static dev . redio . Main . Activity .*; > public class Main { > public static void main ( String [] args ) { > record Pair ( boolean weekend , Activity activity ) {} > var pair = new Pair ( true , new Sleeping ( 8 )); > var msg = switch ( pair ) { // <- A switch expression should have a default case > Java(1073743531) > case Pair ( boolean b , Sleeping s ) -> "3" ; > case Pair ( boolean b , Skiing s ) -> "4" ; > case Pair ( boolean b , Coding c ) -> "5" ; > }; > System . out . println ( msg ); > } > sealed interface Activity { > record Sleeping ( int hours ) implements Activity {} > record Skiing ( String resort ) implements Activity {} > record Coding () implements Activity {} > } > } > This deconstruction should be exhaustive since Pair is a guaranteed match > boolean doesn?t have subclasses and Activity is sealed and fully covered. > Null should throw in this case. A default case would never be reached. > (Compiling and running with (dead) default case) -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Nov 15 15:40:15 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 15 Nov 2022 16:40:15 +0100 (CET) Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> <1142617653.45463508.1668466326897.JavaMail.zimbra@u-pem.fr> Message-ID: <2108854680.46171221.1668526815196.JavaMail.zimbra@u-pem.fr> > From: "John Hendrikx" > To: "Stephen Colebourne" , "Amber dev" > > Sent: Tuesday, November 15, 2022 1:15:27 PM > Subject: Re[2]: Retiring Checked Exceptions Was: Throwing Functions > I just wanted to add an observation here. > I now have seen several ideas here that seem to point to a solution which just > allows lambda's to throw checked exceptions (with a warning or automatically > converted to unchecked), but what I find odd is that this wouldn't solve > anything at all. These functions are almost by definition not used or called > immediately, and so the actual checked exception can occur in a totally > unrelated part of the code that may not be prepared to handle it: this > unrelated code would not be declaring any exceptions and the compiler would > give no warnings or errors, but a checked exception can now pop up there > uninvited. > The simplest example are streams. When you write: > Stream createStream() { > return List.of("file:/xyz").stream().map(URI::new); // throws URISyntaxException > } > There is no need to declare anything, and any warnings or errors would make no > sense. Allowing the above method to declare "URISyntaxException" is just plain > incorrect, leaving you to write a catch block (or rethrow) to deal with > something that can't happen. > Then in a completely unrelated part of the code you may use this stream: > stream.toList(); > There is no sign of the lambda, toList() doesn't declare checked exceptions, but > this can now throw URISyntaxException. > IMHO solutions that just allow you to pretend the checked exception doesn't > exist are going to just muddy the waters further, and don't really solve the > problem at all. They just make it easier to hide the problem, and in doing so, > create new problems. I think it is possible to do better and find a solution > that doesn't come with a new set of problems. You have to compare with what is done currently, when you write this in your IDE Stream createStream() { return List.of("file:/xyz").stream().map(URI::new); // throws URISyntaxException } the IDE will happily transform the code to Stream createStream() { return List.of("file:/xyz").stream().map(s -> { try { return new URI(s); } catch(URISyntaxException e) { throw new RuntimeException(e); } }); } which is in my opinion worst than considering URISyntaxException as unchecked, because here the type of the exception is lost by wrapping it into a runtime exception. So yes, the proposed solution is far from ideal, but i believe a warning is a better answer than wrapping all checked exceptions into unchecked ones. > --John R?mi > ------ Original Message ------ > From "Stephen Colebourne" < [ mailto:scolebourne at joda.org | scolebourne at joda.org > ] > > To "Amber dev" < [ mailto:amber-dev at openjdk.java.net | > amber-dev at openjdk.java.net ] > > Date 15/11/2022 10:41:15 > Subject Re: Retiring Checked Exceptions Was: Throwing Functions >> I outlined simple, practical approach to this when lambdas were first added to >> Java [1]. Much of the blog post above is no longer especially interesting, but >> the basic "lone throws" concept still is. The key part is repeated in these >> three bullet points: >> * Any method may have a throws keyword without specifying the types that are >> thrown ("lone-throws"). This indicates that any exception, checked or unchecked >> may be thrown. Once thrown in this manner, any checked exception flows up the >> stack in an unchecked manner. >> * Any catch clause may have a throws keyword after the catch. This indicates >> that any exception may be caught, even if the exception isn't known to be >> thrown by the try block. >> * All closures are implicitly declared with lone throws. Thus, all closures can >> throw checked and unchecked exceptions without declaring the checked ones. >> I suspect it is too late for the last bullet point to be adopted, but the first >> two would IMO still be hugely beneficial to Java. The point here is not to >> reject the idea of checked exceptions, but to provide a convenient way to >> convert/handle them when they are not what you want. >> Various alternative mechanisms are in use today: >> * additional functional interfaces such as ThrowableFunction >> * utilities like Unchecked.wrap(ThrowableSupplier) which convert checked to >> unchecked >> * hacks like "sneaky throw" >> My argument is that there is a very clear use case in the global corpus of Java >> code for a mechanism to convert/handle checked exceptions more like unchecked. >> And that this would be a good language feature, given that it can be done >> without threatening those who appreciate checked exceptions. >> Stephen >> [1] [ >> https://blog.joda.org/2010/06/exception-transparency-and-lone-throws_9915.html?m=1 >> | >> https://blog.joda.org/2010/06/exception-transparency-and-lone-throws_9915.html?m=1 >> ] >> On Mon, 14 Nov 2022, 23:09 Archie Cobbs, < [ mailto:archie.cobbs at gmail.com | >> archie.cobbs at gmail.com ] > wrote: >>> On Mon, Nov 14, 2022 at 4:52 PM < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr >>> ] > wrote: >>>> I'm proposing to demote checked exceptions to make them second class citizen >>>> because this practically what they are. >>>> The problem is that the status quo is pushing people to use runtime exceptions >>>> instead of exceptions because checked exception do not compose well, exactly >>>> what we both do not want. >>> I totally agree that the lack of composibility (composibleness?) is a problem. >>> But I don't agree that ALL of the blame for that problem rests on checked >>> exceptions. After all, checked exceptions have been around a lot longer than >>> lambdas. >>>> What if checked exceptions work like unchecked casts ? >>> This sounds like a much more promising direction to go in. >>> All we want is a simple way to tell the compiler "I know this lambda can throw a >>> checked exception, just pass it through instead of making this impossible to >>> compile". >>> E.g. something like this?? >>> public void execute( @PassThroughCheckedExceptions Supplier getter) { >>> return getter.get(); >>> } >>> public InputStream openFile(File file) throws IOException { >>> this.execute(FileInputStream::new); >>> } >>> -Archie >>> -- >>> Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From kevinb at google.com Tue Nov 15 16:21:40 2022 From: kevinb at google.com (Kevin Bourrillion) Date: Tue, 15 Nov 2022 08:21:40 -0800 Subject: Retiring Checked Exceptions Was: Throwing Functions In-Reply-To: <2108854680.46171221.1668526815196.JavaMail.zimbra@u-pem.fr> References: <839324466.44117145.1668239937777.JavaMail.zimbra@u-pem.fr> <1142617653.45463508.1668466326897.JavaMail.zimbra@u-pem.fr> <2108854680.46171221.1668526815196.JavaMail.zimbra@u-pem.fr> Message-ID: I agree with Brian that this discussion belongs somewhere other than amber-dev. It would be cool to see this thread die down. :-) On Tue, Nov 15, 2022 at 7:49 AM Remi Forax wrote: > > > ------------------------------ > > *From: *"John Hendrikx" > *To: *"Stephen Colebourne" , "Amber dev" < > amber-dev at openjdk.java.net> > *Sent: *Tuesday, November 15, 2022 1:15:27 PM > *Subject: *Re[2]: Retiring Checked Exceptions Was: Throwing Functions > > I just wanted to add an observation here. > > I now have seen several ideas here that seem to point to a solution which > just allows lambda's to throw checked exceptions (with a warning or > automatically converted to unchecked), but what I find odd is that this > wouldn't solve anything at all. These functions are almost by definition > not used or called immediately, and so the actual checked exception can > occur in a totally unrelated part of the code that may not be prepared to > handle it: this unrelated code would not be declaring any exceptions and > the compiler would give no warnings or errors, but a checked exception can > now pop up there uninvited. > > The simplest example are streams. When you write: > > Stream createStream() { > return List.of("file:/xyz").stream().map(URI::new); // throws > URISyntaxException > } > > There is no need to declare anything, and any warnings or errors would > make no sense. Allowing the above method to declare "URISyntaxException" > is just plain incorrect, leaving you to write a catch block (or rethrow) to > deal with something that can't happen. > > Then in a completely unrelated part of the code you may use this stream: > > stream.toList(); > > There is no sign of the lambda, toList() doesn't declare checked > exceptions, but this can now throw URISyntaxException. > > IMHO solutions that just allow you to pretend the checked exception > doesn't exist are going to just muddy the waters further, and don't really > solve the problem at all. They just make it easier to hide the problem, and > in doing so, create new problems. I think it is possible to do better and > find a solution that doesn't come with a new set of problems. > > > You have to compare with what is done currently, when you write this in > your IDE > > Stream createStream() { > return List.of("file:/xyz").stream().map(URI::new); // throws > URISyntaxException > } > > the IDE will happily transform the code to > > > Stream createStream() { > return List.of("file:/xyz").stream().map(s -> { > try { > return new URI(s); > } catch(URISyntaxException e) { > throw new RuntimeException(e); > } > }); > } > > which is in my opinion worst than considering URISyntaxException as > unchecked, because here the type of the exception is lost by wrapping it > into a runtime exception. > > So yes, the proposed solution is far from ideal, but i believe a warning > is a better answer than wrapping all checked exceptions into unchecked > ones. > > > --John > > > R?mi > > > ------ Original Message ------ > From "Stephen Colebourne" > To "Amber dev" > Date 15/11/2022 10:41:15 > Subject Re: Retiring Checked Exceptions Was: Throwing Functions > > I outlined simple, practical approach to this when lambdas were first > added to Java [1]. Much of the blog post above is no longer especially > interesting, but the basic "lone throws" concept still is. The key part is > repeated in these three bullet points: > > * Any method may have a throws keyword without specifying the types that > are thrown ("lone-throws"). This indicates that any exception, checked or > unchecked may be thrown. Once thrown in this manner, any checked exception > flows up the stack in an unchecked manner. > * Any catch clause may have a throws keyword after the catch. This > indicates that any exception may be caught, even if the exception isn't > known to be thrown by the try block. > * All closures are implicitly declared with lone throws. Thus, all > closures can throw checked and unchecked exceptions without declaring the > checked ones. > > > I suspect it is too late for the last bullet point to be adopted, but the > first two would IMO still be hugely beneficial to Java. The point here is > not to reject the idea of checked exceptions, but to provide a convenient > way to convert/handle them when they are not what you want. > > Various alternative mechanisms are in use today: > * additional functional interfaces such as ThrowableFunction > * utilities like Unchecked.wrap(ThrowableSupplier) which convert checked > to unchecked > * hacks like "sneaky throw" > My argument is that there is a very clear use case in the global corpus of > Java code for a mechanism to convert/handle checked exceptions more like > unchecked. And that this would be a good language feature, given that it > can be done without threatening those who appreciate checked exceptions. > > Stephen > > > [1] > https://blog.joda.org/2010/06/exception-transparency-and-lone-throws_9915.html?m=1 > > On Mon, 14 Nov 2022, 23:09 Archie Cobbs, wrote: > >> On Mon, Nov 14, 2022 at 4:52 PM wrote: >> >>> I'm proposing to demote checked exceptions to make them second class >>> citizen because this practically what they are. >>> The problem is that the status quo is pushing people to use runtime >>> exceptions instead of exceptions because checked exception do not compose >>> well, exactly what we both do not want. >>> >> >> I totally agree that the lack of composibility (composibleness?) is a >> problem. But I don't agree that ALL of the blame for that problem rests on >> checked exceptions. After all, checked exceptions have been around a lot >> longer than lambdas. >> >> >>> What if checked exceptions work like unchecked casts ? >>> >> >> This sounds like a much more promising direction to go in. >> >> All we want is a simple way to tell the compiler "I know this lambda can >> throw a checked exception, just pass it through instead of making this >> impossible to compile". >> >> E.g. something like this?? >> >> public void execute(@PassThroughCheckedExceptions Supplier >> getter) { >> return getter.get(); >> } >> >> public InputStream openFile(File file) throws IOException { >> this.execute(FileInputStream::new); >> } >> >> -Archie >> >> -- >> Archie L. Cobbs >> > > -- Kevin Bourrillion | Java Librarian | Google, Inc. | kevinb at google.com -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Nov 16 11:01:26 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 16 Nov 2022 12:01:26 +0100 (CET) Subject: NPE wrapped in a MatchException when matching a record pattern Message-ID: <1448306333.46578965.1668596486729.JavaMail.zimbra@u-pem.fr> Hi all, Jan and Brian already knows that bug but for the record: This snippet of code should print "B" but currently throw a NPE wrapped into a MatchingException. record Box(E element) {} public static void main(String[] args) { Box> bbs = new Box<>(null); switch (bbs) { case Box>(Box(String s)) -> System.out.println("A"); case Box>(Box b) -> System.out.println("B"); default -> System.out.println("C"); } } Exception in thread "main" java.lang.MatchException: java.lang.NullPointerException: Cannot invoke "Box.element()" because "arg0" is null at Test.$proxy$element(Test.java:6) at Test.main(Test.java:6) Caused by: java.lang.NullPointerException: Cannot invoke "Box.element()" because "arg0" is null ... 2 more regards, R?mi From vab2048 at gmail.com Tue Nov 22 10:42:07 2022 From: vab2048 at gmail.com (Vikram Bakshi) Date: Tue, 22 Nov 2022 10:42:07 +0000 Subject: Relaxing constraint for text blocks to start on new line? Message-ID: Hello, Text blocks are a great feature of the Java language. On the JEP page one of the listed goals of text blocks is to: - - *Enhance the readability of strings in Java programs that denote code written in non-Java languages.* Currently when attempting to use text blocks for short strings which can fit on one line for a non-Java language (e.g. JSON) we still need to start the string on another line. For example: ```java var json = """ { "color": "red", "value": "#f00" } """ ``` It would be a lot nicer to have the ability to use the triple quote to define strings like this on one line: ```java var json = """{ "color": "red", "value": "#f00" } """ ``` Currently that is not possible and using a normal quote for strings, ", requires us to escape all of the subsequent quotes on the same line - which is a real pain. Obviously we can just continue to start on the next line to avoid the issue - but is it possible to consider an extension to text blocks which allow for this use case (short one line strings)? Regards, Vikram -------------- next part -------------- An HTML attachment was scrubbed... URL: From steffen.heinzl at fhws.de Tue Nov 22 21:25:20 2022 From: steffen.heinzl at fhws.de (Heinzl, Steffen) Date: Tue, 22 Nov 2022 21:25:20 +0000 Subject: Another syntax for Java records Message-ID: <6ed0857baed14b68944f6a9598234480@itsc-s164mp.fhws.de> Hi! I wanted to suggest (if not yet done) to another syntax to Java records. I don't quite understand the idea behind the syntax except that some other programming languages do it similar. A Java class looks like this: public class MyClass { String s; void myMethod(String a, int b); } An interface looks like this: public interface MyInterface { void myMethod(String c, String d); } Why all of a sudden is it a good idea to change that syntax to: public record MyRecord(String r, int x) { } instead of the following syntax (which is also possible in C# by the way) public record MyRecord { String r; int x; } I'm teaching Java and I find it strange from a lecturer's point of view to explain the rationale for introducing the syntax like that. I'd be glad if someone could enlighten me! Thanks! Best, Steffen -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 22 21:41:34 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 22 Nov 2022 16:41:34 -0500 Subject: Another syntax for Java records In-Reply-To: <6ed0857baed14b68944f6a9598234480@itsc-s164mp.fhws.de> References: <6ed0857baed14b68944f6a9598234480@itsc-s164mp.fhws.de> Message-ID: To answer your question, we have to go back to "what were we trying to accomplish with records in the first place."? The obvious-but-wrong answer is "to free Java developers of the tyranny of boilerplate."? That's an important benefit, but that's a consequence, not the main goal, of the records design. Another way to say this is that records are not just about "make me the implementation of the data-carrying class that I want"; they are not an "implementation generator" like Lombok or Joda Beans or Immutables or AutoValue.? Yes, they do some implementation lifting, but they also make promises to clients as well. The list of components in the record header -- the state description -- is used to imply a contract: ?- the class has a constructor whose argument list matches exactly the state description ?- the class has a deconstruction pattern whose binding list matches exactly the state description ?- the class has accessors for every component in the state description ?- the class has an equals/hashCode/toString derived from the state description ?- the class adheres to a contract that couples the constructor, accessors, and equals method, which says that if you take apart a record with the accessors, and feed the results back to the constructor, you get back an equivalent (according to equals) record. All of these are derived from the state description, and can be counted on by clients.? That is why the state description is part of the record declaration, not just implied by the fields. (It is also what lets us derive a stronger serialization protocol for records, and in the future, `with` expressions.) This is an example of "get the semantics right and the syntax takes care of itself."? Records have semantics that permits us to boil away the syntax, not the other way around. If that's not enough, here's a less philosophical answer why your approach would be dangerous.? If you could declare ??? record R { ??????? String r; ??????? int x; ??? } then the implied constructor would take a String and an int. But we are used to being able to freely reorder field and method declarations; this has always been a source-, binary-, and behavior-compatible change.? But in your model, reordering fields would reorder the parameters of the constructor, breaking clients (sometimes silently, if you have a record whose fields are all the same type.) On 11/22/2022 4:25 PM, Heinzl, Steffen wrote: > > Hi! > > I wanted to suggest (if not yet done) to another syntax to Java records. > > I don?t quite understand the idea behind the syntax except that some > other programming languages do it similar. > > A Java class looks like this: > > public class MyClass > > { > > ? String s; > > ? void myMethod(String a, int b); > > } > > An interface looks like this: > > public interface MyInterface > > { > > ? void myMethod(String c, String d); > > } > > Why all of a sudden is it a good idea to change that syntax to: > > public record MyRecord(String r, int x) > > { > > } > > instead of the following syntax (which is also possible in C# by the way) > > public record MyRecord > > { > > ? String r; > > ? int x; > > } > > I?m teaching Java and I find it strange from a lecturer?s point of > view to explain the rationale for introducing the syntax like that. > I?d be glad if someone could enlighten me! > > Thanks! Best, > > Steffen > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Nov 22 22:02:17 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 22 Nov 2022 23:02:17 +0100 (CET) Subject: Another syntax for Java records In-Reply-To: <6ed0857baed14b68944f6a9598234480@itsc-s164mp.fhws.de> References: <6ed0857baed14b68944f6a9598234480@itsc-s164mp.fhws.de> Message-ID: <2028521119.51273176.1669154537867.JavaMail.zimbra@u-pem.fr> > From: "Heinzl, Steffen" > To: "amber-dev" > Sent: Tuesday, November 22, 2022 10:25:20 PM > Subject: Another syntax for Java records > Hi! > I wanted to suggest (if not yet done) to another syntax to Java records. > I don?t quite understand the idea behind the syntax except that some other > programming languages do it similar. > A Java class looks like this: > public class MyClass > { > String s; > void myMethod(String a, int b); > } > An interface looks like this: > public interface MyInterface > { > void myMethod(String c, String d); > } > Why all of a sudden is it a good idea to change that syntax to: > public record MyRecord(String r, int x) > { > } > instead of the following syntax (which is also possible in C# by the way) > public record MyRecord > { > String r; > int x; > } > I?m teaching Java and I find it strange from a lecturer?s point of view to > explain the rationale for introducing the syntax like that. I?d be glad if > someone could enlighten me! Conceptually a record defines components which are more than just fields, a component can not be modified, a component has a public accessor (no encapsulation). The canonical constructor, equals/hashCode/toString(), the way to reflect on, use annotation or serialize a record are all defined in term of components. The order of the components is also important for the serialization and the canonical constructor. The name of the components is important for the canonical constructor. So the syntax reflects the fact that a record is defined by a name and its components. see [ https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Record.html | https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Record.html ] > Thanks! Best, > Steffen regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Nov 28 00:07:15 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 27 Nov 2022 19:07:15 -0500 Subject: Could we change the syntax for when clauses? Message-ID: Hello Amber Dev Team, In regards to when clauses, is it possible that we could use a keyword for when clauses that is not the "context-dependent" style keyword? And when I say "context-dependent", I mean that the word "when" is a valid identifier, but we are using context to be able to figure out where it should be treated as a keyword vs. an identifier. The reason I am requesting this is because everytime a context-dependent keyword is added to Java, all the developers who make language/grammar parsing tools for the Java language have to rewrite/uproot nontrivial parts of their code to deal with several finnicky edge cases. It bogs down so much of the parsing logic that a lot of potential optimizations or cleaner solutions go out of the window. This leads to buggy code and/or features not being supported. Much of that could be avoided by choosing a keyword that is not context-dependent. I understand that there does exist a grammar for the language, but because Personally, I'd like it if as a language, Java never used this tactic for any new language feature. But for now, I am only requesting that we use a non-context-dependent phrase to signify when clauses. One suggestion would be to use the "Hyphenated classic keyword" strategy discussed in this JEP Draft (https://openjdk.org/jeps/8223002). Thank you for your time and patience! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Nov 28 00:28:17 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 27 Nov 2022 19:28:17 -0500 Subject: Could we change the syntax for when clauses? Message-ID: Apologies, please ignore the last sentence on the second paragraph. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Nov 28 14:37:56 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 28 Nov 2022 14:37:56 +0000 Subject: Could we change the syntax for when clauses? In-Reply-To: References: Message-ID: <0A016443-4CEE-4C64-8D5F-8C4C54E6AB73@oracle.com> As you point out, this isn?t really about `when`; if we did as you say for `when`, you?d write exactly the same mail the next time we consider a contextual keyword. What you?re really asking is: ?can we please stop using contextual keywords.? (You?ve clearly read https://bugs.openjdk.org/browse/JDK-8223002 (?Keyword Management for the Java Language?), but others reading this exchange may not have, any may benefit from going back and reading it.). Whenever we consider new language syntax, we are called upon to make tradeoffs between many considerations, including: - Compatibility with the existing language. (We could declare `when` to be a real keyword, but that would break any code that uses it as an identifier.) - Naturalness of the improved language. (We could pick a new keyword that is unlikely to conflict with existing code, like `guardedByTheFollowingBooleanCondition`, but writing code in this language would be unpleasant.) - Complexity for tools and specification. (We bear most of this cost, but others in the ecosystem bear some as well.) - Progress vs stagnation. (We could choose not to do guards at all.) The ?keyword management? JEP explicitly acknowledges the challenges with contextual keywords: Cautiously consider contextual keywords At first glance, unitary contextual keywords (and their friends, reserved type names) appear to be a magic wand: they let language designers create the illusion of new keywords without breaking existing programs. However, the positive track record of unitary contextual keywords hides a great deal of complexity and distortion. The process of introducing a unitary contextual keyword is not a simple matter of choosing a word and adding it to the grammar; each one requires an analysis of potential current and future interactions. Each grammar position is its own story: contextual keywords that might be used as modifiers (e.g., readonly) have different ambiguity considerations than those that might be used in code (e.g., a match expression). While a small number of special situations can be managed in a specification or a compiler, the more heavily that unitary contextual keywords are used, the more likely there would be more significant maintenance costs and longer bug tails. Beyond specifications and compilers, unitary contextual keywords distort the language for IDEs. IDEs often have to guess whether an identifier is meant to be an identifier or a unitary contextual keyword, and it may not have enough information to make a good guess until it has seen more input. While this is easy to dismiss as ?not my problem?, in reality, it results in worse code highlighting, auto-completion, and refactoring abilities for everybody. (IDEs have the same trouble with hyphenated contextual keywords too.) Finally, each identifier that is a candidate for dual-purposing as a unitary contextual keyword may have its own special considerations. For example, the use of var as a restricted identifier is justified only because the naming conventions for type names are so broadly adhered to. Using a hyphenated contextual keyword rather than a unitary contextual keyword can sidestep these considerations, since the hyphenated phrase has never been used as an identifier, though the ambiguity issue remains. In summary, unitary contextual keywords are a tool in the language design toolbox, but they should be used with care. Despite these challenges, usability remains paramount. (Remember too that many proposed hyphenated keywords are also contextual, unless one or both parts are existing keywords.). If there were a better candidate that didn?t have these challenges, we would have likely preferred that. We did try &&, which avoided this bullet, but which ultimately fell afoul of usabilty concerns. No credible alternatives were proposed that avoided the problem; even most hyphenated options (e.g., `only-when`) were still contextual, and users would surely have complained ?why do I have to type this long thing.? Did you have a better candidate in mind? On Nov 27, 2022, at 7:07 PM, David Alayachew > wrote: Hello Amber Dev Team, In regards to when clauses, is it possible that we could use a keyword for when clauses that is not the "context-dependent" style keyword? And when I say "context-dependent", I mean that the word "when" is a valid identifier, but we are using context to be able to figure out where it should be treated as a keyword vs. an identifier. The reason I am requesting this is because everytime a context-dependent keyword is added to Java, all the developers who make language/grammar parsing tools for the Java language have to rewrite/uproot nontrivial parts of their code to deal with several finnicky edge cases. It bogs down so much of the parsing logic that a lot of potential optimizations or cleaner solutions go out of the window. This leads to buggy code and/or features not being supported. Much of that could be avoided by choosing a keyword that is not context-dependent. I understand that there does exist a grammar for the language, but because Personally, I'd like it if as a language, Java never used this tactic for any new language feature. But for now, I am only requesting that we use a non-context-dependent phrase to signify when clauses. One suggestion would be to use the "Hyphenated classic keyword" strategy discussed in this JEP Draft (https://openjdk.org/jeps/8223002). Thank you for your time and patience! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Nov 28 16:03:07 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 28 Nov 2022 16:03:07 +0000 Subject: Fwd: On range checks in primitive patterns References: Message-ID: This was received on the amber-spec-comments list. Redirecting to amber-dev as the answers are broadly useful (but as always when bringing these topics to amber-dev, please exercise restraint in replies.) Full message below the line; pull-quotes inline. A number of people seem a little intimidated by the proposed semantics of primitive patterns. These concerns are valid, and the author here is genuinely trying to work through what this new language means. And it is really, really easy to get caught up on the local details, and wonder what it is we are trying to accomplish or why we would have chosen these semantics. First, I?d like to blow up one of the myths holding up many of the concerns that have been raised: that somehow this feature is about ?range checks.? This is an easy conclusion to come to, looking at the examples, and it is a short hop from there to wonder ?but does the language really need a range check feature?? But that is not remotely the purpose of this feature, it just falls out as one of its applications. The author raises the following concern: However, I think the following is a problematic aspect of this model: There is no syntactic difference between a pattern that perform only a variable binding, and a pattern that performs a range check. For example, the following pattern perform a range check if the record component is declared to be `long` but not otherwise: ``` switch (o) { case SomeRecord(int i) -> System.out.println("Match"); ... } ``` This is a valid concern, but has nothing to do with primitive patterns! This is a fundamental aspect of *nested* patterns; some patterns are total and some entail runtime checks, and they may look the same syntactically. (Indeed, people raised similar concerns when we talked about record patterns, specifically about the not-initially-obvious treatment of null in type patterns.) The author?s example could be restated in terms of reference types, and replacing ?range check? with ?null check? or ?type check": switch (o) { Case SomeRecord(String s) -> println(?match?); } Here, as with the OPs example, whether or not there is a runtime check depends on the type of the component of SomeRecord. If SomeRecord?s component is String, there is no runtime check, and even SomeRecord(null) matches; if it is some other type (like Object), a type test is performed, which rejects non-Strings and rejects nulls. The same is true with the primitive pattern; if the type in the pattern declaration and the type at the pattern use site are the same (actually, if one covers the other), then the nested pattern is unconditional and no runtime checks are required, otherwise it is conditional and runtime checks are performed. I realize some people dislike or misunderstand (or both) this semantics; it has an ?action at a distance? that makes some people uncomfortable. (NB: This is not an invitation to reopen this topic, it has been beaten to death.). My point is not ?well, we already commit that sin once, so twice is OK.? My point is a deeper one: we don?t want ?reference type patterns? and ?primitive type patterns?, we want ?type patterns?. This has the following consequences: As you already admit, all of these same consequences are already true with reference type patterns: Admittedly, this situation is similar to how type patterns and sub-classing work already. But I think the problem with integral primitive types is worse, because it is more implicit and easier for programmers to forget about. This gets to the heart of the discomfort: primitives have always been special and ?off to the side", and it is tempting to try to keep them in their own special little box. But the special-ness of primitives is not so great, and its complexity ripples into other features (e.g., we can?t use primitives as generic type parameters, which in turn led to the explosion of IntToLongFunction functional interfaces types). This is why we?re working hard in Valhalla to minimize these differences. So, back to patterns. Why are we doing the non-obvious thing with primitive type patterns? It is about, in part, unifying the treatment of primitives and references to the extent we can ? and we can do a lot here, because the existing rules tell us a lot already about which values can be safely cast to which others. The language already defines complex-but-sensible rules about casting, not only between reference types, but between primitives (including between integral and floating point primitives) and between primitives and references. Only some of these are about range checks. We don?t want to make up new-and-different rules; we want to listen to what the language already says, and if you listen carefully, cast conversion tells you (almost) everything you need to know about `instanceof` with primitives, which in turn tells you everything you need to know about the semantics of type patterns. The current semantics of instanceof ? which is currently restricted to reference types only ? can be viewed as a precondition for safe casting. (Indeed, instanceof and cast are almost always seen together; this is not accidental.) When restricted to reference types, this means subtyping and non-nullity. (We could cast a List to a String, but we?d get an exception; not safe. Similarly we could cast a null to any reference type and it would ?succeed?, but the resulting reference would likely not be safe to use.) When we lift the restriction on ?reference types only?, the same rubric ? safe casting ? tells us exactly what instanceof should mean: can the value be represented exactly in the value set of the specified type. While the existing primitives are not related by subtyping, the rules reflect the reality that 0 and 0L and 0.0D are ?the same number?, because they can be freely converted from int to long to double and back without loss. The proposed semantics of instanceof for primitives, whether or not we call this ?range checking?, reflect this reality. When we wrote the spec for primitive type patterns, it was almost entirely _taking away_ restrictions; the only thing we had to add was defining when a cast was ?lossy? or not, and even that used language that was already present in Ch5 ? ?representable in the range of a type?. But it is not about mere unification of primitives with references (as if that weren?t big enough already). One thing that I see in common with all of these concerns / objections is that they look from the direction of the language we have towards the new feature, rather than looking from the perspective of the language _we want to get to_, and looking backward to the intermediate points. Indeed, when you extrapolate from the language we have to ?what does `instanceof int` mean", it seems that there are multiple possible interpretations, and it is tempting to pick the one that is most comforting. But `instanceof int` is not the feature; it is merely one of the intermediate building blocks of the feature. When viewed from the perspective of the big picture, new constraints emerge on what `instanceof int` might mean that are not obvious when you look at it only from the extrapolative perspective. So if this is not about about mere unification of references and primitives, what it is about? It is about _composition and reversibility_. The language has many mechanisms for creating more complex things from simpler things (e.g., constructors), but fewer mechanisms for going in the opposite direction, and pattern matching aims to fill in these holes. But the constructs we have for aggregation have certain behaviors with respect to composition and conversion; their duals need to have dual behaviors, otherwise we get sharp edges. Here?s an example that doesn?t involve primitives: record R(Object o) { ? } String s = ? R r = new R(s) ? If (r instanceof R(String t)) { ? } R can hold an Object. Why are we allowed to instantiate it with a String, which is a different type? Because JLS 5.3 (?Invocation Context?) says that in a method invocation context, a reference widening conversion (subtyping) may be performed on method parameters. When we go to reverse this aggregation with a pattern match, the nested pattern `String s` is not total on the type of the component (Object). This means a runtime check will be done, which will match if the value is not null and a narrowing reference conversion (casting) would succeed without error. (This is the same as `instnaceof`, which is the safe casting precondition.). We didn?t pick these semantics at random; we picked them because we want for pattern matching to be able to take apart what the constructor invocation puts together, using the same conversions (and similar syntax.). This leads us to an interpretation of the pattern match which is: ?could this value have come from invoking the R constructor with a String.? (This can?t be guaranteed by the language, since constructors can do weird things, but the intuition is really useful.). We can make the exactly analogous situation with primitives: record S(long x) { ? } int y = ? S s = new R(y) ? If (s instanceof S(long z)) { ? } Why are we allowed to invoke the constructor of S, which takes long, with an int? Because JLS 5.3 also allows _primitive widening conversions_ here. But, if we did as you suggest (as others have): * Only allow simple variable bindings in type patterns for integral types. Don't do range checking in patterns at all. (To me this seems like the best approach.) then we can reverse the construction of R, and ask ?did this come from invoking R with a String?, but we can?t reverse the construction of S and ask ?did this come from invoking S with an int.? That?s terrible ? it perpetuates the sharp edge between primitives and references into places that users will constantly be cut on. Here?s another thing that fails with the narrow interpretation: record G(T t) { } G g = ? If (g instanceof G(int x)) { ? } If `int` were only applicable to `int`, we couldn?t nest the `int x` pattern here; we?d have to nest `Integer x`, and convert explicitly (risking NPE.). We want to be able to compose these tests and conversions. (Some have suggested a variant of your rule, which is to allow unboxing conversions but not primitive widening conversions. But this is the worst kind of ad-hoc, preference-based tinkering, because it creates a new context with a new and different set of conversions from any other context. That?s pure incremental complexity.) Similarly, because aggregation already composes nicely, we want destructuring to compose in the same way. We can pack an R in an Optional: Optional o = Optional.of(new R(?foo?)); merely by composing method invocations. We want destructuring to compose in the same way: If (o instanceof Optional.of(R(String s)) { ? } so that our intuition ? could this have come from the corresponding aggregation expression ? is preserved. This imposes strong constraints constraints about the meaning of pattern composition, as well as the treatment of conversions in pattern uses, so they can mean the dual of the corresponding aggregation expression. Jumping up yet another level, this tendency to look at things from the extrapolating-forward perspective rather than the big-picture perspective illustrates one of the challenges of the rapid cadence (which otherwise has been a huge success.). In the old big-bang days, we would have released pattern matching in one big atomic transaction, with all the pieces in place on day 1, and it would have been more clear how they fit together. Instead, we are releasing them in increments (which provides some value earlier, and is probably easier to learn), but the flip side is that it becomes less obvious where we are going. (We are still careful to design far ahead enough of the releases to know it is going to fit together, as we did in the old days, but have been able to incrementally ship parts of it along the way.) But it is harder to see the big picture by looking at the next increment. But rest assured, there is a big picture here. So the feature here is not ?how should primitives work in instanceof? or ?how should primitive type patterns work?; it is ?how should patterns fit together to compose in a way that they compose cleanly and are the dual of the existing aggregation features of the language.? While this may appear to make the language locally more complicated when you look at it as ?we?re adding new rules about primitive type patterns", when you look at the big picture it actually makes it _simpler_ because there?s one set of rules that applies to everything, and fewer gratuitous sharp corners where things work differently. Instanceof derives from safe casting; type patterns derive from instanceof; patterns compose the same way as the corresponding aggregation construct, with the duals of the corresponding conversions. There?s no localized, ad-hoc tinkering with ?how should primitive instanceof work? ? it works the way it does because this is the semantics that give us composition and reversibility with a consistent set of rules, derived from rules we already have (e.g., cast conversion.). Begin forwarded message: From: Jens Lidestr?m > Subject: On range checks in primitive patterns Date: November 27, 2022 at 7:29:04 AM EST To: amber-spec-comments at openjdk.java.net Dear Amber expert group, I have followed the discussion on primitive patterns and wish to make a comment. The expert group is most certainty already aware of the issue, but I don't know if it has been discussed enough and I wish to draw attention to it. It seems to me that there are attractive aspects of the current proposal in [1], in which a pattern match operation on integral types perform a range check on its operand, so that `i instanceof byte b` matches if `i` is in the range of a byte. However, I think the following is a problematic aspect of this model: There is no syntactic difference between a pattern that perform only a variable binding, and a pattern that performs a range check. For example, the following pattern perform a range check if the record component is declared to be `long` but not otherwise: ``` switch (o) { case SomeRecord(int i) -> System.out.println("Match"); ... } ``` This has the following consequences: * When writing the pattern it's easy to make mistakes, forgetting to check the type of the record component, and out of habit declare the type in the pattern as `int`. If the record happens to contain a `long` this become a range check by mistake. This will often not be caught by the compiler and the pattern will simply not match in unexpected ways. * When updating the type of the record component, for example from `int` to `long`, it is easy to forget that this might change patterns. Patterns that previously only performed a variable binding now makes a range check. This will often not be caught by the compiler. Admittedly, this situation is similar to how type patterns and sub-classing work already. But I think the problem with integral primitive types is worse, because it is more implicit and easier for programmers to forget about. This problem is also somewhat similar to how total patterns accepts null while non-total patterns rejects null. I think these kinds of semantic differences between similar-looking constructs is something to be very wary about in programming languages! Possible solutions: * Only allow simple variable bindings in type patterns for integral types. Don't do range checking in patterns at all. (To me this seems like the best approach.) * Find an alternative syntax for patterns that perform range checks. (This is related to a suggestion from Tagir Valeev to this list on 2022-11-16 17:50.) Thank you, expert group, for your good work! It it truly a treat for a language enthusiast to be able to follow your discussions on these lists. Best regards, Jens Lidestr?m [1]: https://bugs.openjdk.org/browse/JDK-8288476 -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Nov 28 18:26:55 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 28 Nov 2022 13:26:55 -0500 Subject: Could we change the syntax for when clauses? Message-ID: Hello Brian, Thank you for your response. > As you point out, this isn?t really about `when` Guilty as charged. I see contextual keywords as a tactic that is useful when there is a phrase that is so expressive/useful that it is worth the costs I mentioned in the original post. There's definitely a time and a place for them. However, I also feel like the budget for that has been mostly exhausted already, and that from here on out, I'd like us to move away from this tactic. > if we did as you say for `when`, you?d write exactly > the same mail the next time we consider a contextual > keyword. To be clear, I certainly didn't and do not intend to raise this point again without something major changing. I just felt like now was a useful time to bring up this discussion because "when clauses" are in preview. Also, I get the feeling that the keyword decision made here will likely set the tempo for keyword choices moving forward. For example, it looks like we'll all be seeing a lot more "switches" in Java's near future. So if "when clauses" are received well, I am pretty certain that people will use them as evidence to support using more contextual keywords in the future. You can imagine my concern. Therefore, I'd like to catch it now while it's still within reach. > but others reading this exchange may not have (read > the JEP Draft) Thank you for pointing this out. I agree, and I will also provide a brief summary here too. In short, this JEP Draft takes advantage of 3 facts and combines them together to create a new way to generate new Java keywords that are NOT context-dependent. First, there are "classic keywords" in Java that can NEVER be used as an identifier. Here are a few. final int double if case Second, the hyphen (-) in Java is currently used to denote subtraction (ignore the "non-sealed" keyword for now). Third, you can ONLY perform subtraction upon identifiers, NEVER on "classic keywords". Therefore, if you combine these 3 facts, you can use the following strategy to be able to create new keywords. The official name the JEP used is "Hyphenated classic keywords". If you make a keyword like "record-class" "record" is considered a valid identifier, but "class" can NEVER be a valid identifier. Therefore, if we follow the pattern of IDENTIFIER --> HYPHEN --> KEYWORD you can generate all sorts of keywords that are NOT context-dependent. This allows all the language parser folks to simply look for a "classic keyword", see if it is followed by or following a hyphen, then check for the permitted "Hyphenated classic keywords". And to be clear, the JEP Draft also suggests other strategies as well that instead use context-dependent concepts. I am not talking about those, only the paragraph of the JEP Draft titled "Hyphenated classic keywords". > Whenever we consider new language syntax, we are > called upon to make tradeoffs between many > considerations This was very informative, thank you. I had soft concepts for all of this, but this explanation helped solidify where they apply and how we reason about them. Thank you. > (We bear most of this cost, but others in the > ecosystem bear some as well.) Thank you for highlighting this. Yes, none of my criticism here is to belittle the scale of work being done. I want to make sure that we take the steps that will cause us all the least amount of pain moving forward. > Despite these challenges, usability remains paramount. > (Remember too that many proposed hyphenated keywords > are also contextual, unless one or both parts are > existing keywords.). Thank you for highlighting this. Yes, I think my suggestion contributes nothing unless we use the "Hyphenated classic keywords" strategy. Otherwise, we end up right back in the same hole with a different shovel. > If there were a better candidate that didn?t have these > challenges, we would have likely preferred that. We did > try &&, which avoided this bullet, but which ultimately > fell afoul of usabilty concerns. Good to know. Could you tell me roughly the time when these discussions happened on the mailing lists? I try very hard to research these things before posting, but there's no search bar on the mailing list, and google is rarely much help. Perhaps I need to get better at googling? > No credible alternatives were proposed that avoided the > problem; even most hyphenated options (e.g., `only-when`) > were still contextual, and users would surely have > complained ?why do I have to type this long thing.? I can imagine the Amber team's frustration. To be frank, the fact that it is not 2 characters or less is going to cause some users to complain about length. I struggle to find the motivation to appease them with my suggestions. > Did you have a better candidate in mind? Here are a few. NONE of these suggestions are context-dependent (at least, not in the way I have been describing thus far) since they all use a "classic keyword" as part of the phrase. - if ---------- Yes, I am proposing to repurpose the if statement here. The concept that the "when clause" represents has strong similarities to an if statement, so maybe this would allow us to build off of users existing mental models? And it should appease all of the 2 char, code golf enthusiasts as well. - case-guard -- Verbose, but clear. No confusion on what this is or means. If expressiveness/clarity was the only priority, this would likely be the best choice. - case-if ----- The middle ground between the above 2. - only-if ----- Since "only-when" was suggested before, maybe this one? It's no longer context-dependent now. There are others to be found as well. I believe the below link has all of the "classic keywords"? We can run through more permutations if none of mine are good. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html <------ Someone correct me if this list is not exhaustive or if it is out of date. Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Nov 28 19:13:41 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 28 Nov 2022 14:13:41 -0500 Subject: Could we change the syntax for when clauses? In-Reply-To: <0A016443-4CEE-4C64-8D5F-8C4C54E6AB73@oracle.com> References: <0A016443-4CEE-4C64-8D5F-8C4C54E6AB73@oracle.com> Message-ID: (copying my message here since I responded to the wrong thing) Hello Brian, Thank you for your response. > As you point out, this isn?t really about `when` Guilty as charged. I see contextual keywords as a tactic that is useful when there is a phrase that is so expressive/useful that it is worth the costs I mentioned in the original post. There's definitely a time and a place for them. However, I also feel like the budget for that has been mostly exhausted already, and that from here on out, I'd like us to move away from this tactic. > if we did as you say for `when`, you?d write exactly > the same mail the next time we consider a contextual > keyword. To be clear, I certainly didn't and do not intend to raise this point again without something major changing. I just felt like now was a useful time to bring up this discussion because "when clauses" are in preview. Also, I get the feeling that the keyword decision made here will likely set the tempo for keyword choices moving forward. For example, it looks like we'll all be seeing a lot more "switches" in Java's near future. So if "when clauses" are received well, I am pretty certain that people will use them as evidence to support using more contextual keywords in the future. You can imagine my concern. Therefore, I'd like to catch it now while it's still within reach. > but others reading this exchange may not have (read > the JEP Draft) Thank you for pointing this out. I agree, and I will also provide a brief summary here too. In short, this JEP Draft takes advantage of 3 facts and combines them together to create a new way to generate new Java keywords that are NOT context-dependent. First, there are "classic keywords" in Java that can NEVER be used as an identifier. Here are a few. final int double if case Second, the hyphen (-) in Java is currently used to denote subtraction (ignore the "non-sealed" keyword for now). Third, you can ONLY perform subtraction upon identifiers, NEVER on "classic keywords". Therefore, if you combine these 3 facts, you can use the following strategy to be able to create new keywords. The official name the JEP used is "Hyphenated classic keywords". If you make a keyword like "record-class" "record" is considered a valid identifier, but "class" can NEVER be a valid identifier. Therefore, if we follow the pattern of IDENTIFIER --> HYPHEN --> KEYWORD you can generate all sorts of keywords that are NOT context-dependent. This allows all the language parser folks to simply look for a "classic keyword", see if it is followed by or following a hyphen, then check for the permitted "Hyphenated classic keywords". And to be clear, the JEP Draft also suggests other strategies as well that instead use context-dependent concepts. I am not talking about those, only the paragraph of the JEP Draft titled "Hyphenated classic keywords". > Whenever we consider new language syntax, we are > called upon to make tradeoffs between many > considerations This was very informative, thank you. I had soft concepts for all of this, but this explanation helped solidify where they apply and how we reason about them. Thank you. > (We bear most of this cost, but others in the > ecosystem bear some as well.) Thank you for highlighting this. Yes, none of my criticism here is to belittle the scale of work being done. I want to make sure that we take the steps that will cause us all the least amount of pain moving forward. > Despite these challenges, usability remains paramount. > (Remember too that many proposed hyphenated keywords > are also contextual, unless one or both parts are > existing keywords.). Thank you for highlighting this. Yes, I think my suggestion contributes nothing unless we use the "Hyphenated classic keywords" strategy. Otherwise, we end up right back in the same hole with a different shovel. > If there were a better candidate that didn?t have these > challenges, we would have likely preferred that. We did > try &&, which avoided this bullet, but which ultimately > fell afoul of usabilty concerns. Good to know. Could you tell me roughly the time when these discussions happened on the mailing lists? I try very hard to research these things before posting, but there's no search bar on the mailing list, and google is rarely much help. Perhaps I need to get better at googling? > No credible alternatives were proposed that avoided the > problem; even most hyphenated options (e.g., `only-when`) > were still contextual, and users would surely have > complained ?why do I have to type this long thing.? I can imagine the Amber team's frustration. To be frank, the fact that it is not 2 characters or less is going to cause some users to complain about length. I struggle to find the motivation to appease them with my suggestions. > Did you have a better candidate in mind? Here are a few. NONE of these suggestions are context-dependent (at least, not in the way I have been describing thus far) since they all use a "classic keyword" as part of the phrase. - if ---------- Yes, I am proposing to repurpose the if statement here. The concept that the "when clause" represents has strong similarities to an if statement, so maybe this would allow us to build off of users existing mental models? And it should appease all of the 2 char, code golf enthusiasts as well. - case-guard -- Verbose, but clear. No confusion on what this is or means. If expressiveness/clarity was the only priority, this would likely be the best choice. - case-if ----- The middle ground between the above 2. - only-if ----- Since "only-when" was suggested before, maybe this one? It's no longer context-dependent now. There are others to be found as well. I believe the below link has all of the "classic keywords"? We can run through more permutations if none of mine are good. https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html <------ Someone correct me if this list is not exhaustive or if it is out of date. Thank you for your time and insight! David Alayachew On Mon, Nov 28, 2022 at 9:38 AM Brian Goetz wrote: > As you point out, this isn?t really about `when`; if we did as you say for > `when`, you?d write exactly the same mail the next time we consider a > contextual keyword. What you?re really asking is: ?can we please stop > using contextual keywords.? (You?ve clearly read > https://bugs.openjdk.org/browse/JDK-8223002 (?Keyword Management for the > Java Language?), but others reading this exchange may not have, any may > benefit from going back and reading it.). > > Whenever we consider new language syntax, we are called upon to make > tradeoffs between many considerations, including: > > - Compatibility with the existing language. (We could declare `when` to > be a real keyword, but that would break any code that uses it as an > identifier.) > - Naturalness of the improved language. (We could pick a new keyword > that is unlikely to conflict with existing code, like > `guardedByTheFollowingBooleanCondition`, but writing code in this language > would be unpleasant.) > - Complexity for tools and specification. (We bear most of this cost, > but others in the ecosystem bear some as well.) > - Progress vs stagnation. (We could choose not to do guards at all.) > > The ?keyword management? JEP explicitly acknowledges the challenges with > contextual keywords: > > *Cautiously consider contextual keywords* > > At first glance, unitary contextual keywords (and their friends, reserved > type names) appear to be a magic wand: they let language designers create > the illusion of new keywords without breaking existing programs. However, > the positive track record of unitary contextual keywords hides a great deal > of complexity and distortion. > > The process of introducing a unitary contextual keyword is not a simple > matter of choosing a word and adding it to the grammar; each one requires > an analysis of potential current and future interactions. Each grammar > position is its own story: contextual keywords that might be used as > modifiers (e.g., readonly) have different ambiguity considerations than > those that might be used in code (e.g., a match expression). While a > small number of special situations can be managed in a specification or a > compiler, the more heavily that unitary contextual keywords are used, the > more likely there would be more significant maintenance costs and longer > bug tails. > > Beyond specifications and compilers, unitary contextual keywords distort > the language for IDEs. IDEs often have to guess whether an identifier is > meant to be an identifier or a unitary contextual keyword, and it may not > have enough information to make a good guess until it has seen more input. > While this is easy to dismiss as ?not my problem?, in reality, it results > in worse code highlighting, auto-completion, and refactoring abilities for > everybody. (IDEs have the same trouble with hyphenated contextual keywords > too.) > > Finally, each identifier that is a candidate for dual-purposing as a > unitary contextual keyword may have its own special considerations. For > example, the use of var as a restricted identifier is justified only > because the naming conventions for type names > are > so broadly adhered to. Using a hyphenated contextual keyword rather than a > unitary contextual keyword can sidestep these considerations, since the > hyphenated phrase has never been used as an identifier, though the > ambiguity issue remains. > > In summary, unitary contextual keywords are a tool in the language design > toolbox, but they should be used with care. > > > Despite these challenges, usability remains paramount. (Remember too that > many proposed hyphenated keywords are also contextual, unless one or both > parts are existing keywords.). > > If there were a better candidate that didn?t have these challenges, we > would have likely preferred that. We did try &&, which avoided this > bullet, but which ultimately fell afoul of usabilty concerns. No credible > alternatives were proposed that avoided the problem; even most hyphenated > options (e.g., `only-when`) were still contextual, and users would surely > have complained ?why do I have to type this long thing.? > > Did you have a better candidate in mind? > > > On Nov 27, 2022, at 7:07 PM, David Alayachew > wrote: > > Hello Amber Dev Team, > > In regards to when clauses, is it possible that we could use a keyword for > when clauses that is not the "context-dependent" style keyword? And when I > say "context-dependent", I mean that the word "when" is a valid identifier, > but we are using context to be able to figure out where it should be > treated as a keyword vs. an identifier. > > The reason I am requesting this is because everytime a context-dependent > keyword is added to Java, all the developers who make language/grammar > parsing tools for the Java language have to rewrite/uproot nontrivial parts > of their code to deal with several finnicky edge cases. It bogs down so > much of the parsing logic that a lot of potential optimizations or cleaner > solutions go out of the window. This leads to buggy code and/or features > not being supported. Much of that could be avoided by choosing a keyword > that is not context-dependent. I understand that there does exist a grammar > for the language, but because > > Personally, I'd like it if as a language, Java never used this tactic for > any new language feature. But for now, I am only requesting that we use a > non-context-dependent phrase to signify when clauses. One suggestion would > be to use the "Hyphenated classic keyword" strategy discussed in this JEP > Draft (https://openjdk.org/jeps/8223002). > > Thank you for your time and patience! > David Alayachew > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Nov 28 19:23:24 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 28 Nov 2022 19:23:24 +0000 Subject: Could we change the syntax for when clauses? In-Reply-To: References: Message-ID: Also, I get the feeling that the keyword decision made here will likely set the tempo for keyword choices moving forward. For example, it looks like we'll all be seeing a lot more "switches" in Java's near future. So if "when clauses" are received well, I am pretty certain that people will use them as evidence to support using more contextual keywords in the future. You can imagine my concern. Therefore, I'd like to catch it now while it's still within reach. Well, contextual keywords have been a thing for a long time, so to some degree, its already no longer ?within reach.? But I don?t think we should worry too much about a flood; each feature is carefully considered and we try to balance all the competing concerns. We agree that we should use them with care. I think where we disagree is the weights we assign in the balancing of the tradeoffs, and specifically, how we balance what is ?good for the users? with what is ?good for the implementers.? The purpose of the keywords JEP was (a) to outline the space we are working within today, and (b) expand into a new space which may give us more options. For example, we could decide to use ?double? instead of when: case Integer x double x > 0: But that would be pretty silly, because it makes no sense. There is limited ability to overload existing keywords and operators within the bounds of maintaining a reasonable language. The hyphenation gives us a broader range of non-contextual keywords, which increases the chance we?ll find something good. But this would also be bad: case Integer x double-checking-that x > 0: Because it would be clear that we?re only using `double` because its a keyword, not because it makes sense here. We have more options, but we still have to use sense and taste to not pick the bad ones. Moving down the scale, hyphenated contextual keywords (e.g., non-sealed) are probably fine in some contexts (e.g., class declarations) and less fine in others. (Everything its its own consideration.). And they don?t make anything easier for parser writers anyway. Second, the hyphen (-) in Java is currently used to denote subtraction (ignore the "non-sealed" keyword for now). Let?s not ignore non-sealed. There are two kinds of hyphenated keywords: those that can be true keywords because one or both words is already a keyword and therefore the dash could not be subtraction (e.g., non-final), and those where neither word is a keyword and therefore both could be identifiers (these are contextual hyphenated keywords.). The latter is still harder for parser writers. Therefore, if we follow the pattern of IDENTIFIER --> HYPHEN --> KEYWORD you can generate all sorts of keywords that are NOT context-dependent. This allows all the language parser folks to simply look for a "classic keyword", see if it is followed by or following a hyphen, then check for the permitted "Hyphenated classic keywords". Yes, but I think you?re overvaluing the power of this as a generator. There?s just not that many keywords, and therefore most of the ones that this generator produces are going to be silly like double-checking-when. Good to know. Could you tell me roughly the time when these discussions happened on the mailing lists? I try very hard to research these things before posting, but there's no search bar on the mailing list, and google is rarely much help. Perhaps I need to get better at googling? The sands of time often resist excavation... > No credible alternatives were proposed that avoided the > problem; even most hyphenated options (e.g., `only-when`) > were still contextual, and users would surely have > complained ?why do I have to type this long thing.? I can imagine the Amber team's frustration. To be frank, the fact that it is not 2 characters or less is going to cause some users to complain about length. I struggle to find the motivation to appease them with my suggestions. In a community of 10M users, we?re not aiming for unanimity, because that?s impossible. But yes, you can?t please all the people all of the time. We aim for decisions that, ten years from now, don?t stand out. (We often struggle with Stroustrup?s rule here too.). Here are a few. NONE of these suggestions are context-dependent (at least, not in the way I have been describing thus far) since they all use a "classic keyword" as part of the phrase. - if ---------- Yes, I am proposing to repurpose the if statement here. The concept that the "when clause" represents has strong similarities to an if statement, so maybe this would allow us to build off of users existing mental models? And it should appease all of the 2 char, code golf enthusiasts as well. This one was discussed at some length and was rejected with prejudice :) - case-guard -- Verbose, but clear. No confusion on what this is or means. If expressiveness/clarity was the only priority, this would likely be the best choice. - case-if ----- The middle ground between the above 2. - only-if ----- Since "only-when" was suggested before, maybe this one? It's no longer context-dependent now. I will admit the last one ?isn?t terrible?, though it definitely STANDS OUT (see ?Stroustrup?s Rule?) and I suspect the ten-year test will prefer ?when? pretty strongly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Nov 28 21:49:58 2022 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 28 Nov 2022 16:49:58 -0500 Subject: Could we change the syntax for when clauses? In-Reply-To: References: Message-ID: Hello Brian, Thank you for the response. Also, apologies for the multiple branches - sometimes pipermail confuses me. > Contextual keywords have been a thing for a long time, > so to some degree, its already no longer ?within > reach.? But I don?t think we should worry too much > about a flood; each feature is carefully considered > and we try to balance all the competing concerns. Of course, but the number and scale of them have been ramping up sharply lately. I agree that every decision made regarding contextual keywords was well reasoned and thought through, I am only trying to raise the point that the effort to carry this is getting heavier at a faster rate for parsing folks. > We agree that we should use them with care. I think > where we disagree is the weights we assign in the > balancing of the tradeoffs. That makes sense. I think I understand the perspective that you are coming from now - simplifying the developer experience by maximizing readability and expressiveness while avoiding unnecessary verbosity or contradiction of the user's existing mental model. For context, I am coming from the perspective of making language analysis tools for the language as easy to make and performant as possible. I also teach new developers how to code in Java, and one of the consistent pain points has been learning when and where a context dependent keyword is allowed. Both of these points are what motivated me to make my original post. > double-checking-that This is a very helpful example. It sounds like you are saying it is the wrong choice because it throws away the established mental model to create a new meaning. (double as a number, versus double as in the phrase double-checking) To make sure that I understand it though, another example would be something like this? case Integer x while x > 0 Right? That would "check all the boxes", but would involve users throwing away the definition to make room for another one. (while as in while LOOP, versus while as in the english word to imply 2 things being true simultaneously) It also sounds like not only do we want something that checks all the boxes, but almost reads like a sentence too. Understanding that, it helps me see why "when" is a good fit. > We aim for decisions that, ten years from now, don?t > stand out. (We often struggle with Stroustrup?s rule > here too.) I never knew about Stroustrup's Rule, thank you. Here it is for those who are also just learning it. - For new features, people insist on LOUD explicit syntax. - For established features, people want terse notation. With the point being, what is explicit and helpful today becomes noisy and verbose tomorrow. We should prioritize terse now, and then it will feel natural later. This makes a lot of sense. And in that case, I see why my suggestions mostly don't meet the needs. There needs to be a delicate balance between terse and expressive here. > (In regards to using "if") > This one was discussed at some length and was > rejected with prejudice :) I'll do some research and see if that one isn't lost to the sands of time as well. If anyone has a link or a rough sense of timing, that would also be helpful. Alternatively, if anyone remembers the reason, I would love to know why. I can pretty well guess, but want to confirm. > (In regards to using "only-if") > I will admit the last one ?isn?t terrible?, > though it definitely STANDS OUT (see > Stroustrup?s Rule) and I suspect the > ten-year test will prefer ?when? pretty > strongly. After reading all of this, I can see how it might stand out. And looking at the above points again, I also see why "only-if" is the best contender of my suggestions. - Doesn't deviate too far from an existing mental model - Is somewhat terse - Reads like a sentence I still stand behind "only-if". That said, I definitely concede that, from the everyday Java developer's perspective, "when" is the superior choice. All the same, as the language inevitably grows and branches out into new territory/use-cases, the need for stronger static analysis tools grows with it. I'd like to avoid stepping in the way of that any more than necessary. And I think the benefits gained are worth 2 more characters and a hyphen. Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From vab2048 at gmail.com Wed Nov 30 10:09:20 2022 From: vab2048 at gmail.com (Vikram Bakshi) Date: Wed, 30 Nov 2022 10:09:20 +0000 Subject: Relaxing constraint for text blocks to start on new line? In-Reply-To: References: Message-ID: Hi all, Does anybody on the mailing list want to bite and answer the message I had sent earlier? Even if it is to say "no we will not be doing that because " - that would suffice! Regards, Vikram On Tue, Nov 22, 2022 at 10:42 AM Vikram Bakshi wrote: > Hello, > > Text blocks are a great feature of the Java language. On the JEP page one > of the listed goals of text blocks is to: > > > - *Enhance the readability of strings in Java programs that denote > code written in non-Java languages.* > > > Currently when attempting to use text blocks for short strings which can > fit on one line for a non-Java language (e.g. JSON) we still need to start > the string on another line. > > For example: > > ```java > var json = """ > { "color": "red", "value": "#f00" } """ > ``` > > It would be a lot nicer to have the ability to use the triple quote to > define strings like this on one line: > > ```java > var json = """{ "color": "red", "value": "#f00" } """ > ``` > > Currently that is not possible and using a normal quote for strings, ", > requires us to escape all of the subsequent quotes on the same line - which > is a real pain. > > Obviously we can just continue to start on the next line to avoid the > issue - but is it possible to consider an extension to text blocks which > allow for this use case (short one line strings)? > > Regards, > Vikram > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Nov 30 11:09:38 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 30 Nov 2022 12:09:38 +0100 (CET) Subject: Relaxing constraint for text blocks to start on new line? In-Reply-To: References: Message-ID: <1298710788.56331625.1669806578209.JavaMail.zimbra@u-pem.fr> Sorry to not answer sooner, the reason to not allow anything after the first """ is that we may add modifiers after the """ in the future, by example, if we do not want the character '\' to be escaped, in the future you may able to write String text = """ no-escape some text with a \n """; regards, R?mi > From: "Vikram Bakshi" > To: "amber-dev" > Sent: Tuesday, November 22, 2022 11:42:07 AM > Subject: Relaxing constraint for text blocks to start on new line? > Hello, > Text blocks are a great feature of the Java language. On the JEP page one of the > listed goals of text blocks is to: > * > * Enhance the readability of strings in Java programs that denote code written > in non-Java languages. > Currently when attempting to use text blocks for short strings which can fit on > one line for a non-Java language (e.g. JSON) we still need to start the string > on another line. > For example: > ```java > var json = """ > { "color": "red", "value": "#f00" } """ > ``` > It would be a lot nicer to have the ability to use the triple quote to define > strings like this on one line: > ```java > var json = """{ "color": "red", "value": "#f00" } """ > ``` > Currently that is not possible and using a normal quote for strings, ", requires > us to escape all of the subsequent quotes on the same line - which is a real > pain. > Obviously we can just continue to start on the next line to avoid the issue - > but is it possible to consider an extension to text blocks which allow for this > use case (short one line strings)? > Regards, > Vikram -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Wed Nov 30 11:11:45 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Wed, 30 Nov 2022 11:11:45 +0000 Subject: Relaxing constraint for text blocks to start on new line? In-Reply-To: References: Message-ID: Technically there is no reason why we couldn?t. It would, however, be difficult to call the result a text ?block?. Thick quotes? The answer is simply ?reserved for future use?. The rationale is to provide the block structure that you?ve come to know and leave a space where we could in future we could put additional information describing the content. Cheers, ? Jim ? On Nov 30, 2022, at 6:10 AM, Vikram Bakshi wrote: ? Hi all, Does anybody on the mailing list want to bite and answer the message I had sent earlier? Even if it is to say "no we will not be doing that because " - that would suffice! Regards, Vikram On Tue, Nov 22, 2022 at 10:42 AM Vikram Bakshi > wrote: Hello, Text blocks are a great feature of the Java language. On the JEP page one of the listed goals of text blocks is to: * Enhance the readability of strings in Java programs that denote code written in non-Java languages. Currently when attempting to use text blocks for short strings which can fit on one line for a non-Java language (e.g. JSON) we still need to start the string on another line. For example: ```java var json = """ { "color": "red", "value": "#f00" } """ ``` It would be a lot nicer to have the ability to use the triple quote to define strings like this on one line: ```java var json = """{ "color": "red", "value": "#f00" } """ ``` Currently that is not possible and using a normal quote for strings, ", requires us to escape all of the subsequent quotes on the same line - which is a real pain. Obviously we can just continue to start on the next line to avoid the issue - but is it possible to consider an extension to text blocks which allow for this use case (short one line strings)? Regards, Vikram -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Wed Nov 30 12:19:22 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Wed, 30 Nov 2022 13:19:22 +0100 Subject: Relaxing constraint for text blocks to start on new line? In-Reply-To: References: Message-ID: Hello! I'm always confused with multi-line literals in Kotlin, like val myXml = """ !!! """.trimIndent() Here, trimIndent does nothing, and the indentation still persists, because of the first line. You can easily miss it, as it's not aligned to the rest. I think disallowing starting the literal on the first line in Java was the right decision, assuming that most of the applications are actually multi-line literals, not just the way to avoid escaping of ". With best regards, Tagir Valeev. On Wed, Nov 30, 2022 at 12:11 PM Jim Laskey wrote: > > Technically there is no reason why we couldn?t. It would, however, be difficult to call the result a text ?block?. Thick quotes? > > The answer is simply ?reserved for future use?. The rationale is to provide the block structure that you?ve come to know and leave a space where we could in future we could put additional information describing the content. > > Cheers, > > ? Jim > > > > ? > > On Nov 30, 2022, at 6:10 AM, Vikram Bakshi wrote: > > ? > Hi all, > > Does anybody on the mailing list want to bite and answer the message I had sent earlier? > > Even if it is to say "no we will not be doing that because " - that would suffice! > > Regards, > Vikram > > > On Tue, Nov 22, 2022 at 10:42 AM Vikram Bakshi wrote: >> >> Hello, >> >> Text blocks are a great feature of the Java language. On the JEP page one of the listed goals of text blocks is to: >> >> Enhance the readability of strings in Java programs that denote code written in non-Java languages. >> >> >> Currently when attempting to use text blocks for short strings which can fit on one line for a non-Java language (e.g. JSON) we still need to start the string on another line. >> >> For example: >> >> ```java >> var json = """ >> { "color": "red", "value": "#f00" } """ >> ``` >> >> It would be a lot nicer to have the ability to use the triple quote to define strings like this on one line: >> >> ```java >> var json = """{ "color": "red", "value": "#f00" } """ >> ``` >> >> Currently that is not possible and using a normal quote for strings, ", requires us to escape all of the subsequent quotes on the same line - which is a real pain. >> >> Obviously we can just continue to start on the next line to avoid the issue - but is it possible to consider an extension to text blocks which allow for this use case (short one line strings)? >> >> Regards, >> Vikram