From archie.cobbs at gmail.com Fri Jul 12 20:23:52 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 12 Jul 2024 15:23:52 -0500 Subject: Try/Catch DU Exception Message-ID: I'd like to propose a very minor language improvement and would appreciate any feedback. This is a true corner case, but I bet most developers have tripped over it a few times. It's easy to work around... but still... Here's a simple example: void describeMyThread(Thread thread) { final String description; try { description = '"' + thread.getName() + "'"; } catch (NullPointerException e) { description = "null"; } System.out.println("the thread is " + description); } This doesn't compile: DUTest.java:8: error: variable description might already have been assigned description = "(null)"; ^ The error is a false positive: there is no way an exception can be thrown in the try block *after* description is assigned, because description being assigned is literally the last thing that occurs in the try block. Developers intuitively know that description will be DU at the start of the catch block, so the error feels surprising and makes the compiler seem less smart than it should be. My proposal is to fix this by adding a "try/catch DU exception" to ?16.2.15: V is definitely unassigned before a catch block iff all of the following are true: V is definitely unassigned after the try block *or the try block ends with an assignment expression statement V=b and V is definitely unassigned after b* V is definitely unassigned before every return statement ... A prototype compiler implementation is here . Thoughts? -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Fri Jul 12 20:43:40 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Fri, 12 Jul 2024 22:43:40 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: I personally wouldn't like too much if the compiler tried to be very smart with this. The current behavior is very easy to reason about, and this proposal looks too special to me. Especially because then it raises more questions about `Thread.stop`. Also, the way this issue is presented is not much of a concern (at least for the example code). Not making the local variable final is kinda whatever. Where this is annoying is when you want to capture the variable (mostly in a lambda). However, if that is the issue that you are trying to address, then I think this is the wrong solution to that issue. I think the real solution to the issue would be relaxing the constraint to be able to capture a local variable: We should be able to capture it, iff the variable is definitely assigned before capturing, and there is definitely no assignment after it. Archie Cobbs ezt ?rta (id?pont: 2024. j?l. 12., P, 22:24): > I'd like to propose a very minor language improvement and would appreciate > any feedback. > > This is a true corner case, but I bet most developers have tripped over it > a few times. It's easy to work around... but still... > > Here's a simple example: > > void describeMyThread(Thread thread) { > final String description; > try { > description = '"' + thread.getName() + "'"; > } catch (NullPointerException e) { > description = "null"; > } > System.out.println("the thread is " + description); > } > > This doesn't compile: > > DUTest.java:8: error: variable description might already have been > assigned > description = "(null)"; > ^ > > The error is a false positive: there is no way an exception can be thrown > in the try block *after* description is assigned, because description > being assigned is literally the last thing that occurs in the try block. > > Developers intuitively know that description will be DU at the start of > the catch block, so the error feels surprising and makes the compiler seem > less smart than it should be. > > My proposal is to fix this by adding a "try/catch DU exception" to > ?16.2.15: > > V is definitely unassigned before a catch block iff all of the following > are true: > V is definitely unassigned after the try block *or the try block ends > with an assignment expression statement V=b and V is definitely unassigned > after b* > V is definitely unassigned before every return statement ... > > A prototype compiler implementation is here > > . > > Thoughts? > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Fri Jul 12 22:59:10 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Sat, 13 Jul 2024 01:59:10 +0300 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: I do not agree with Attila on this one. I think there is nothing "very smart" about compiler knowing can some variable be possibly/for sure uninitialized, especially in distant advents of non-null types from valhalla. I would love to see this feature in language. Moreover, this new rule may be event more loose if talking about checked exceptions: variable could possibly be initialized if and only if it's initialization at least in one execution branch happens before last call to method that throws checked exception in catch block, i.e. // assuming "created" is a final variable try { created = iThrowIOException(); // blah blah blah that does not throw IOException } catch (IOEception _) { created = false; } In this example, the last possible operation to throw IOException happens before variable initialization, so variable is definitely unassigned. Also note that this does not only apply to final local variables, but also to final fields assignment in constructors or static blocks. I see this smartening of the compiler as a good step ahead in direction of making language more friendly and simultaneously flexible, without introducing any complex rules. PS: I am currently unable to test provided code myself, but if ode would compile fine without assignment in catch block, this is a serious vulnerability because compiler let's through possibly uninitialized final variable. On Fri, Jul 12, 2024, 23:44 Attila Kelemen wrote: > I personally wouldn't like too much if the compiler tried to be very smart > with this. The current behavior is very easy to reason about, and this > proposal looks too special to me. Especially because then it raises more > questions about `Thread.stop`. > > Also, the way this issue is presented is not much of a concern (at least > for the example code). Not making the local variable final is kinda > whatever. Where this is annoying is when you want to capture the variable > (mostly in a lambda). However, if that is the issue that you are trying to > address, then I think this is the wrong solution to that issue. I think the > real solution to the issue would be relaxing the constraint to be able to > capture a local variable: We should be able to capture it, iff the variable > is definitely assigned before capturing, and there is definitely no > assignment after it. > > Archie Cobbs ezt ?rta (id?pont: 2024. j?l. 12., > P, 22:24): > >> I'd like to propose a very minor language improvement and would >> appreciate any feedback. >> >> This is a true corner case, but I bet most developers have tripped over >> it a few times. It's easy to work around... but still... >> >> Here's a simple example: >> >> void describeMyThread(Thread thread) { >> final String description; >> try { >> description = '"' + thread.getName() + "'"; >> } catch (NullPointerException e) { >> description = "null"; >> } >> System.out.println("the thread is " + description); >> } >> >> This doesn't compile: >> >> DUTest.java:8: error: variable description might already have been >> assigned >> description = "(null)"; >> ^ >> >> The error is a false positive: there is no way an exception can be thrown >> in the try block *after* description is assigned, because description >> being assigned is literally the last thing that occurs in the try block. >> >> Developers intuitively know that description will be DU at the start of >> the catch block, so the error feels surprising and makes the compiler seem >> less smart than it should be. >> >> My proposal is to fix this by adding a "try/catch DU exception" to >> ?16.2.15: >> >> V is definitely unassigned before a catch block iff all of the following >> are true: >> V is definitely unassigned after the try block *or the try block >> ends with an assignment expression statement V=b and V is definitely >> unassigned after b* >> V is definitely unassigned before every return statement ... >> >> A prototype compiler implementation is here >> >> . >> >> Thoughts? >> >> -Archie >> >> -- >> Archie L. Cobbs >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Jul 13 04:50:35 2024 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 13 Jul 2024 06:50:35 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Hello! This problem indeed bugs me and not only me. E.g., IntelliJ IDEA users occasionally come and ask why we highlight such a variable as mutable: https://youtrack.jetbrains.com/issue/IDEA-193255/Java-local-variable-highlighed-as-reassigned https://youtrack.jetbrains.com/issue/IDEA-200048/Highlighting-for-reassigned-variables-exclude-variables-which-are-assigned-in-try-catch If we support it, it should probably work for final fields initialization in constructors as well. With best regards, Tagir Valeev On Fri, Jul 12, 2024 at 10:24?PM Archie Cobbs wrote: > I'd like to propose a very minor language improvement and would appreciate > any feedback. > > This is a true corner case, but I bet most developers have tripped over it > a few times. It's easy to work around... but still... > > Here's a simple example: > > void describeMyThread(Thread thread) { > final String description; > try { > description = '"' + thread.getName() + "'"; > } catch (NullPointerException e) { > description = "null"; > } > System.out.println("the thread is " + description); > } > > This doesn't compile: > > DUTest.java:8: error: variable description might already have been > assigned > description = "(null)"; > ^ > > The error is a false positive: there is no way an exception can be thrown > in the try block *after* description is assigned, because description > being assigned is literally the last thing that occurs in the try block. > > Developers intuitively know that description will be DU at the start of > the catch block, so the error feels surprising and makes the compiler seem > less smart than it should be. > > My proposal is to fix this by adding a "try/catch DU exception" to > ?16.2.15: > > V is definitely unassigned before a catch block iff all of the following > are true: > V is definitely unassigned after the try block *or the try block ends > with an assignment expression statement V=b and V is definitely unassigned > after b* > V is definitely unassigned before every return statement ... > > A prototype compiler implementation is here > > . > > Thoughts? > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sat Jul 13 05:02:31 2024 From: amaembo at gmail.com (Tagir Valeev) Date: Sat, 13 Jul 2024 07:02:31 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Hello! On Fri, Jul 12, 2024 at 10:44?PM Attila Kelemen wrote: > I personally wouldn't like too much if the compiler tried to be very smart > with this. The current behavior is very easy to reason about, and this > proposal looks too special to me. Especially because then it raises more > questions about `Thread.stop`. > Thread.stop is not an issue anymore, as it's dysfunctional since Java 18: https://bugs.openjdk.org/browse/JDK-8277861 It still could be possible to generate an exception in the thread using JVM TI though. On the other hand, you can update final local variable using JVM TI as well, so it should not be a problem. > Also, the way this issue is presented is not much of a concern (at least > for the example code). Not making the local variable final is kinda > whatever. Where this is annoying is when you want to capture the variable > (mostly in a lambda). However, if that is the issue that you are trying to > address, then I think this is the wrong solution to that issue. I think the > real solution to the issue would be relaxing the constraint to be able to > capture a local variable: We should be able to capture it, iff the variable > is definitely assigned before capturing, and there is definitely no > assignment after it. > > Archie Cobbs ezt ?rta (id?pont: 2024. j?l. 12., > P, 22:24): > >> I'd like to propose a very minor language improvement and would >> appreciate any feedback. >> >> This is a true corner case, but I bet most developers have tripped over >> it a few times. It's easy to work around... but still... >> >> Here's a simple example: >> >> void describeMyThread(Thread thread) { >> final String description; >> try { >> description = '"' + thread.getName() + "'"; >> } catch (NullPointerException e) { >> description = "null"; >> } >> System.out.println("the thread is " + description); >> } >> >> This doesn't compile: >> >> DUTest.java:8: error: variable description might already have been >> assigned >> description = "(null)"; >> ^ >> >> The error is a false positive: there is no way an exception can be thrown >> in the try block *after* description is assigned, because description >> being assigned is literally the last thing that occurs in the try block. >> >> Developers intuitively know that description will be DU at the start of >> the catch block, so the error feels surprising and makes the compiler seem >> less smart than it should be. >> >> My proposal is to fix this by adding a "try/catch DU exception" to >> ?16.2.15: >> >> V is definitely unassigned before a catch block iff all of the following >> are true: >> V is definitely unassigned after the try block *or the try block >> ends with an assignment expression statement V=b and V is definitely >> unassigned after b* >> V is definitely unassigned before every return statement ... >> >> A prototype compiler implementation is here >> >> . >> >> Thoughts? >> >> -Archie >> >> -- >> Archie L. Cobbs >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sat Jul 13 09:54:19 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sat, 13 Jul 2024 11:54:19 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Thanks, it is good to see `Thread.stop` go (though as far as I can see it was only made dysfunctional in JDK 20). That said, I think my main point still stands. It is a complicated rule to completely explain. A clear evidence to this is that Archie's original definition is certainly not what people would expect for this rule, because they would expect this to compile then: ``` final int x; try { if (cond1) x = f1(); else x = f2(); } catch (Exception e) { x = f3(); } ``` where the last statement of the `try` block is not an assignment statement. Also, my real concern here is that people are not pointing out where this is truly annoying. Because the reason you need that final so much because you want this to work: ``` final int x; try { x = 4; } catch (Exception e) { x = 2; } executor.execute(() -> System.out.println(x)); ``` If so, then this "compiler fix" is a very special case, and not solving the true issue. Since it does not solve the following (which is equally annoying): ``` int x = 4; if (something()) { x = 2; } executor.execute(() -> System.out.println(x)); // No more assignments to `x` ``` Even though this could be solved by naturally relaxing the constraint on captured local variables, and in that case not many people would complain about Archie's issue in my opinion/ Tagir Valeev ezt ?rta (id?pont: 2024. j?l. 13., Szo, 7:02): > Hello! > > On Fri, Jul 12, 2024 at 10:44?PM Attila Kelemen < > attila.kelemen85 at gmail.com> wrote: > >> I personally wouldn't like too much if the compiler tried to be very >> smart with this. The current behavior is very easy to reason about, and >> this proposal looks too special to me. Especially because then it raises >> more questions about `Thread.stop`. >> > > Thread.stop is not an issue anymore, as it's dysfunctional since Java 18: > https://bugs.openjdk.org/browse/JDK-8277861 > It still could be possible to generate an exception in the thread using > JVM TI though. On the other hand, you can update final local variable using > JVM TI as well, so it should not be a problem. > > >> Also, the way this issue is presented is not much of a concern (at least >> for the example code). Not making the local variable final is kinda >> whatever. Where this is annoying is when you want to capture the variable >> (mostly in a lambda). However, if that is the issue that you are trying to >> address, then I think this is the wrong solution to that issue. I think the >> real solution to the issue would be relaxing the constraint to be able to >> capture a local variable: We should be able to capture it, iff the variable >> is definitely assigned before capturing, and there is definitely no >> assignment after it. >> >> Archie Cobbs ezt ?rta (id?pont: 2024. j?l. 12., >> P, 22:24): >> >>> I'd like to propose a very minor language improvement and would >>> appreciate any feedback. >>> >>> This is a true corner case, but I bet most developers have tripped over >>> it a few times. It's easy to work around... but still... >>> >>> Here's a simple example: >>> >>> void describeMyThread(Thread thread) { >>> final String description; >>> try { >>> description = '"' + thread.getName() + "'"; >>> } catch (NullPointerException e) { >>> description = "null"; >>> } >>> System.out.println("the thread is " + description); >>> } >>> >>> This doesn't compile: >>> >>> DUTest.java:8: error: variable description might already have been >>> assigned >>> description = "(null)"; >>> ^ >>> >>> The error is a false positive: there is no way an exception can be >>> thrown in the try block *after* description is assigned, because >>> description being assigned is literally the last thing that occurs in >>> the try block. >>> >>> Developers intuitively know that description will be DU at the start of >>> the catch block, so the error feels surprising and makes the compiler seem >>> less smart than it should be. >>> >>> My proposal is to fix this by adding a "try/catch DU exception" to >>> ?16.2.15: >>> >>> V is definitely unassigned before a catch block iff all of the following >>> are true: >>> V is definitely unassigned after the try block *or the try block >>> ends with an assignment expression statement V=b and V is definitely >>> unassigned after b* >>> V is definitely unassigned before every return statement ... >>> >>> A prototype compiler implementation is here >>> >>> . >>> >>> Thoughts? >>> >>> -Archie >>> >>> -- >>> Archie L. Cobbs >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Sat Jul 13 11:16:37 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Sat, 13 Jul 2024 14:16:37 +0300 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: It would be good to fix what you are saying. However, I don't agree that this enhancement would introduce any new rule. Rule for final variables and fields is one and only one in this context: it must be initialized once. This fix would just make enforcement of this rule more precise by modifying existing compiler rules that currently don't let through code that is intuitively and factually correct, but falls into a blind spot of specifications. Of course, JLS defines a set of rules on how to determine whether the value was previously assigned or not, and, strictly speaking, current behaviour is the right one. However, JLS here fails to let through valid code from, lets say, a logical point of view. So, actually, no one introduces new rules to the compiler. In fact, this fix would remove exception from the rules that are semantically applied to final variables. On Sat, Jul 13, 2024 at 12:54?PM Attila Kelemen wrote: > Thanks, it is good to see `Thread.stop` go (though as far as I can see it > was only made dysfunctional in JDK 20). > > That said, I think my main point still stands. It is a complicated rule to > completely explain. A clear evidence to this is that Archie's original > definition is certainly not what people would expect for this rule, because > they would expect this to compile then: > > ``` > final int x; > try { > if (cond1) x = f1(); > else x = f2(); > } catch (Exception e) { > x = f3(); > } > ``` > > where the last statement of the `try` block is not an assignment statement. > > Also, my real concern here is that people are not pointing out where this > is truly annoying. Because the reason you need that final so much because > you want this to work: > > ``` > final int x; > try { > x = 4; > } catch (Exception e) { > x = 2; > } > > executor.execute(() -> System.out.println(x)); > ``` > > If so, then this "compiler fix" is a very special case, and not solving > the true issue. Since it does not solve the following (which is equally > annoying): > > ``` > int x = 4; > if (something()) { > x = 2; > } > > executor.execute(() -> System.out.println(x)); > // No more assignments to `x` > ``` > > Even though this could be solved by naturally relaxing the constraint on > captured local variables, and in that case not many people would complain > about Archie's issue in my opinion/ > > Tagir Valeev ezt ?rta (id?pont: 2024. j?l. 13., Szo, > 7:02): > >> Hello! >> >> On Fri, Jul 12, 2024 at 10:44?PM Attila Kelemen < >> attila.kelemen85 at gmail.com> wrote: >> >>> I personally wouldn't like too much if the compiler tried to be very >>> smart with this. The current behavior is very easy to reason about, and >>> this proposal looks too special to me. Especially because then it raises >>> more questions about `Thread.stop`. >>> >> >> Thread.stop is not an issue anymore, as it's dysfunctional since Java 18: >> https://bugs.openjdk.org/browse/JDK-8277861 >> It still could be possible to generate an exception in the thread using >> JVM TI though. On the other hand, you can update final local variable using >> JVM TI as well, so it should not be a problem. >> >> >>> Also, the way this issue is presented is not much of a concern (at least >>> for the example code). Not making the local variable final is kinda >>> whatever. Where this is annoying is when you want to capture the variable >>> (mostly in a lambda). However, if that is the issue that you are trying to >>> address, then I think this is the wrong solution to that issue. I think the >>> real solution to the issue would be relaxing the constraint to be able to >>> capture a local variable: We should be able to capture it, iff the variable >>> is definitely assigned before capturing, and there is definitely no >>> assignment after it. >>> >>> Archie Cobbs ezt ?rta (id?pont: 2024. j?l. >>> 12., P, 22:24): >>> >>>> I'd like to propose a very minor language improvement and would >>>> appreciate any feedback. >>>> >>>> This is a true corner case, but I bet most developers have tripped over >>>> it a few times. It's easy to work around... but still... >>>> >>>> Here's a simple example: >>>> >>>> void describeMyThread(Thread thread) { >>>> final String description; >>>> try { >>>> description = '"' + thread.getName() + "'"; >>>> } catch (NullPointerException e) { >>>> description = "null"; >>>> } >>>> System.out.println("the thread is " + description); >>>> } >>>> >>>> This doesn't compile: >>>> >>>> DUTest.java:8: error: variable description might already have been >>>> assigned >>>> description = "(null)"; >>>> ^ >>>> >>>> The error is a false positive: there is no way an exception can be >>>> thrown in the try block *after* description is assigned, because >>>> description being assigned is literally the last thing that occurs in >>>> the try block. >>>> >>>> Developers intuitively know that description will be DU at the start >>>> of the catch block, so the error feels surprising and makes the compiler >>>> seem less smart than it should be. >>>> >>>> My proposal is to fix this by adding a "try/catch DU exception" to >>>> ?16.2.15: >>>> >>>> V is definitely unassigned before a catch block iff all of the >>>> following are true: >>>> V is definitely unassigned after the try block *or the try block >>>> ends with an assignment expression statement V=b and V is definitely >>>> unassigned after b* >>>> V is definitely unassigned before every return statement ... >>>> >>>> A prototype compiler implementation is here >>>> >>>> . >>>> >>>> Thoughts? >>>> >>>> -Archie >>>> >>>> -- >>>> Archie L. Cobbs >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sat Jul 13 11:38:46 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sat, 13 Jul 2024 13:38:46 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: I guess the main difference in our thinking is that I value the complexity of the JLS more. The informal rule you are talking about (i.e., assigned exactly once) might appear to be simple but it is in reality impossible to prove completely and there is no end to complicating the formal rule. I mean, I showed a simple complication in my previous email already but let's go further. Why shouldn't the following work then? ``` final int x; int i = 0; try { x = func(); i++; } catch (Exception e) { x = -1; } ``` or even ``` int end = 1; final int x; for (int i = 0; i < end; i++) { x = 5; } ``` All of the above would be equally possible for the compiler to deduce to be correct. Complicating the formal rule matters a lot for the language in my opinion. And of course, you could complicate the language but then I would expect some major gain from it. What do we gain here? That we can add the `final` keyword to a local variable declaration? That doesn't seem very valuable to me (assuming the variable capturing rule is relaxed as I wrote). Olexandr Rotan ezt ?rta (id?pont: 2024. j?l. 13., Szo, 13:16): > It would be good to fix what you are saying. However, I don't agree that > this enhancement would introduce any new rule. Rule for final variables and > fields is one and only one in this context: it must be initialized once. > This fix would just make enforcement of this rule more precise by modifying > existing compiler rules that currently don't let through code that is > intuitively and factually correct, but falls into a blind spot of > specifications. Of course, JLS defines a set of rules on how to determine > whether the value was previously assigned or not, and, strictly speaking, > current behaviour is the right one. However, JLS here fails to let through > valid code from, lets say, a logical point of view. So, actually, no one > introduces new rules to the compiler. In fact, this fix would remove > exception from the rules that are semantically applied to final variables. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sat Jul 13 11:55:38 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sat, 13 Jul 2024 13:55:38 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Adding a little bit extra on top of that: I believe that if Java ever wants to go down this route, then it should first develop the notion of "nothrows" (or maybe even a strong constraint on what is allowed to be thrown), and then on top of that the flow analysis could be enhanced. I'm not saying I'm eager for this to happen but I think that would be the bare minimum before this issue should be considered to be fixed. Attila Kelemen ezt ?rta (id?pont: 2024. j?l. 13., Szo, 13:38): > I guess the main difference in our thinking is that I value the complexity > of the JLS more. The informal rule you are talking about (i.e., assigned > exactly once) might appear to be simple but it is in reality impossible to > prove completely and there is no end to complicating the formal rule. I > mean, I showed a simple complication in my previous email already but let's > go further. Why shouldn't the following work then? > > ``` > final int x; > int i = 0; > try { > x = func(); > i++; > } catch (Exception e) { > x = -1; > } > ``` > > or even > > ``` > int end = 1; > final int x; > for (int i = 0; i < end; i++) { > x = 5; > } > ``` > > All of the above would be equally possible for the compiler to deduce to > be correct. Complicating the formal rule matters a lot for the language in > my opinion. And of course, you could complicate the language but then I > would expect some major gain from it. What do we gain here? That we can add > the `final` keyword to a local variable declaration? That doesn't seem very > valuable to me (assuming the variable capturing rule is relaxed as I wrote). > > Olexandr Rotan ezt ?rta (id?pont: 2024. j?l. > 13., Szo, 13:16): > >> It would be good to fix what you are saying. However, I don't agree that >> this enhancement would introduce any new rule. Rule for final variables and >> fields is one and only one in this context: it must be initialized once. >> This fix would just make enforcement of this rule more precise by modifying >> existing compiler rules that currently don't let through code that is >> intuitively and factually correct, but falls into a blind spot of >> specifications. Of course, JLS defines a set of rules on how to determine >> whether the value was previously assigned or not, and, strictly speaking, >> current behaviour is the right one. However, JLS here fails to let through >> valid code from, lets say, a logical point of view. So, actually, no one >> introduces new rules to the compiler. In fact, this fix would remove >> exception from the rules that are semantically applied to final variables. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Sat Jul 13 15:06:53 2024 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Sat, 13 Jul 2024 16:06:53 +0100 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Would not that be addressed with the suggested switch-case-throws? See https://inside.java/2023/12/15/switch-case-effect/ final String description = switch(thread.getName()) { case String name -> '"' + name + '"'; case throws NullPointerException _ -> "null"; } On Fri, 12 Jul 2024 at 21:24, Archie Cobbs wrote: > I'd like to propose a very minor language improvement and would appreciate > any feedback. > > This is a true corner case, but I bet most developers have tripped over it > a few times. It's easy to work around... but still... > > Here's a simple example: > > void describeMyThread(Thread thread) { > final String description; > try { > description = '"' + thread.getName() + "'"; > } catch (NullPointerException e) { > description = "null"; > } > System.out.println("the thread is " + description); > } > > This doesn't compile: > > DUTest.java:8: error: variable description might already have been > assigned > description = "(null)"; > ^ > > The error is a false positive: there is no way an exception can be thrown > in the try block *after* description is assigned, because description > being assigned is literally the last thing that occurs in the try block. > > Developers intuitively know that description will be DU at the start of > the catch block, so the error feels surprising and makes the compiler seem > less smart than it should be. > > My proposal is to fix this by adding a "try/catch DU exception" to > ?16.2.15: > > V is definitely unassigned before a catch block iff all of the following > are true: > V is definitely unassigned after the try block *or the try block ends > with an assignment expression statement V=b and V is definitely unassigned > after b* > V is definitely unassigned before every return statement ... > > A prototype compiler implementation is here > > . > > Thoughts? > > -Archie > > -- > Archie L. Cobbs > -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Sat Jul 13 16:45:32 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sat, 13 Jul 2024 11:45:32 -0500 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Thanks for the feedback. Following up... The current behavior is very easy to reason about, and this proposal looks > too special to me.... I guess the main difference in our thinking is that I > value the complexity of the JLS more. Yes, this definitely falls into the category of "tactical fix". It's a specific (small) fix for a specific (small) corner case. You raise a good question re: whether we should be looking at a bigger picture, for example your if/then/else example. That would mean replacing "last statement of the block is V=x" with something more general. My concern is that extending this idea in that way would necessarily be much more invasive to the spec. Instead of defining and tracking two things (DA and DU) for every possible expression, the JLS would now have to track three things: DA, DU, and "DU for the purpose of catch". Chapter 16 would expand by 50%. So this changes the cost/benefit equation by a lot. Or... maybe there's some simpler trick that would accomplish the same thing? If so I don't immediately see it. Especially because then it raises more questions about `Thread.stop`. Like what? In the source code, the catch block still can't *use* the variable, because it's still not DA. All that's changed is that it can now *assign* the variable. Also, the way this issue is presented is not much of a concern (at least > for the example code). Not making the local variable final is kinda > whatever. That's a personal preference thing. For me making variables final is important for improving code readability and I do it literally whenever possible. > Where this is annoying is when you want to capture the variable (mostly in > a lambda). However, if that is the issue that you are trying to address, > then I think this is the wrong solution to that issue. I think the real > solution to the issue would be relaxing the constraint to be able to > capture a local variable: We should be able to capture it, iff the variable > is definitely assigned before capturing, and there is definitely no > assignment after it. That's *not* the issue I'm trying to address, but I totally agree with you that it would also be a good one to have. Are you officially proposing it? If so, I support you :) Would not that be addressed with the suggested switch-case-throws? See > https://inside.java/2023/12/15/switch-case-effect/ > That's a workaround, yes, but not a fix. This fix would just make enforcement of this rule more precise by modifying > existing compiler rules that currently don't let through code that is > intuitively and factually correct, but falls into a blind spot of > specifications. ... [separate email] ... I mean, I showed a simple > complication in my previous email already but let's go further. Why > shouldn't the following work then? So to summarize what I think we can all agree on: - In an ideal world, the compiler would perfectly/precisely identify any situation where a final variable was not assigned exactly once. - Due to the halting problem we know this is impossible. The best the JLS can do is define a reasonable approximation, and everyone understands that the rules are just an approximation. - What we have does a pretty good job but it's not perfect. It will never be perfect. Life is full of such situations :) In these situations we typically make incremental improvements over time, pragmatically, on a cost/benefit basis. So to me the question is not "What are the *correct* rules for DA/DU" - no such rules exist - but where are there opportunities to do the normal incremental improvement thing where the cost/benefit ratio is favorable. For this proposal, it's a one line JLS change. Like I said, a small fix for a small corner case. I'd also support the more general version, as long as I don't have to be the one who updates Chapter 16 :) I also think replacing the "effectively final" requirement for lambdas & inner classes to be "DA at the point of instantiation and not reassigned afterward" would be another good one, but that's a separate proposal... -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sat Jul 13 17:35:10 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sat, 13 Jul 2024 19:35:10 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: > > Yes, this definitely falls into the category of "tactical fix". It's a > specific (small) fix for a specific (small) corner case. > > You raise a good question re: whether we should be looking at a bigger > picture, for example your if/then/else example. That would mean replacing > "last statement of the block is V=x" with something more general. > > My concern is that extending this idea in that way would necessarily be > much more invasive to the spec. Instead of defining and tracking two things > (DA and DU) for every possible expression, the JLS would now have to track > three things: DA, DU, and "DU for the purpose of catch". Chapter 16 would > expand by 50%. > > So this changes the cost/benefit equation by a lot. > > Or... maybe there's some simpler trick that would accomplish the same > thing? If so I don't immediately see it. > Maybe somewhat more invasive to the spec but I don't think it would be the place you are referring to. So, if the spec had the concept of "nothrows" (i.e., when an expression / statement cannot throw an exception), then you "just" needed to change the flow analysis. It would not need "DU for the purpose of the catch". I didn't try to formalize it but I don't see it more complicated than a break / continue of a loop. At least in this case I would be mostly neutral about the change because it would be conceptually more compact. My problem with this special case is that it is rather arbitrary to just rule that this is important and others are not, and if things get "fixed" on a case by case basis, then I worry that the language rule will effectively evolve into: "Let's type it in, and see if the compiler is ok with it". Which pretty much the case for generic type inference, and that doesn't have a good track record for being bug free. In fact, I remember reporting one bug in it on the javac list, and the response I got was basically that "we are aware but it is too complicated to fix" (and I can empathize with that). I would avoid going down the route of special rules for something like this. Especially since it is actually rare (though annoying of course), and the workaround is mostly trivial (you can factor that out into a method). > Especially because then it raises more questions about `Thread.stop`. > > > Like what? In the source code, the catch block still can't *use* the > variable, because it's still not DA. All that's changed is that it can now > *assign* the variable. > I guess given Tagir's comment that `Thread.stop` is not a thing now, it is a non-issue. But if it still worked, then the question you would be asking is: "Can an exception be raised after the last statement but before the end of the `try` block?". To be honest I was never sure if that was possible or not, and I hated to think about the possibility of `Thread.stop` until I just accepted that I won't because it is practically impossible to write a code that is guaranteed to remain consistent after an asynchronous exception. > Where this is annoying is when you want to capture the variable (mostly in >> a lambda). However, if that is the issue that you are trying to address, >> then I think this is the wrong solution to that issue. I think the real >> solution to the issue would be relaxing the constraint to be able to >> capture a local variable: We should be able to capture it, iff the variable >> is definitely assigned before capturing, and there is definitely no >> assignment after it. > > > That's *not* the issue I'm trying to address, but I totally agree with > you that it would also be a good one to have. Are you officially proposing > it? If so, I support you :) > I'm not sure what counts as official but I would much certainly welcome such a change. I have always found it silly that you can't capture a local variable just because it took multiple steps to initialize. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Sat Jul 13 21:18:08 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sat, 13 Jul 2024 16:18:08 -0500 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: On Sat, Jul 13, 2024 at 12:35?PM Attila Kelemen wrote: > Or... maybe there's some simpler trick that would accomplish the same >> thing? If so I don't immediately see it. >> > > Maybe somewhat more invasive to the spec but I don't think it would be the > place you are referring to. So, if the spec had the concept of "nothrows" > (i.e., when an expression / statement cannot throw an exception), then you > "just" needed to change the flow analysis. It would not need "DU for the > purpose of the catch". I didn't try to formalize it but I don't see it more > complicated than a break / continue of a loop. At least in this case I > would be mostly neutral about the change because it would be > conceptually more compact. > OK let's try going down this route just for fun... :) Here's a sketch of how the spec might accommodate this. For any type E extending Throwble and expression or statement X, first define whether "X might throw E" as follows: - (Precise Special Exceptions) If E is assignable from one of: ArithmeticException, ArrayIndexOutOfBoundsException, ArrayStoreException, ClassCastException, AssertionError, ExceptionInInitializerError, MatchException, NegativeArraySizeException, NullPointerException, (others?) and X is one of those expressions which the JLS defines to possibly throw that exception, then X might throw E. - (Imprecise Special Exceptions) If E is assignable from one of: LinkageError (or any of its defined subclasses), BootstrapMethodError, StackOverflowError, InternalError, OutOfMemoryError, ThreadDeath (others?), then X might throw E. - (Checked Exceptions) If E extends neither Error nor RuntimeException, and X is an invocation of a method that declares it throws something assignable to E, then X might throw Ei. - (Unchecked Exceptions) If E extends Error or RuntimeException, and X is an invocation of a method, then X might throw E. Now consider a catch block that catches E? | E? | ... Then the new rule would be: - For any variable V, then V is definitely [un]assigned at the start of the catch block iff the following is true for every E?: for every X in the try block that might throw E?, V is definitely [un]assigned before X. So most of the new complexity here seems to be in dealing with all the "special exception" cases. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sat Jul 13 21:53:39 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sat, 13 Jul 2024 23:53:39 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: > > OK let's try going down this route just for fun... :) > > Here's a sketch of how the spec might accommodate this. > > For any type E extending Throwble and expression or statement X, first > define whether "X might throw E" as follows: > > - (Precise Special Exceptions) If E is assignable from one of: > ArithmeticException, ArrayIndexOutOfBoundsException, ArrayStoreException, > ClassCastException, AssertionError, ExceptionInInitializerError, > MatchException, NegativeArraySizeException, NullPointerException, (others?) > and X is one of those expressions which the JLS defines to possibly throw > that exception, then X might throw E. > - (Imprecise Special Exceptions) If E is assignable from one of: > LinkageError (or any of its defined subclasses), BootstrapMethodError, > StackOverflowError, InternalError, OutOfMemoryError, ThreadDeath (others?), > then X might throw E. > - (Checked Exceptions) If E extends neither Error nor > RuntimeException, and X is an invocation of a method that declares it > throws something assignable to E, then X might throw Ei. > - (Unchecked Exceptions) If E extends Error or RuntimeException, and X > is an invocation of a method, then X might throw E. > > Now consider a catch block that catches E? | E? | ... > > Then the new rule would be: > > - For any variable V, then V is definitely [un]assigned at the start > of the catch block iff the following is true for every E?: for every X in > the try block that might throw E?, V is definitely [un]assigned before X. > > So most of the new complexity here seems to be in dealing with all the > "special exception" cases. > > That formalization looks very nice to me. Definitely better than the foggy idea I had in my head. And what is most appealing to me is that it actually cleanly completes the flow to the catch blocks including both DA and DU. So, given that, even this would work (not very useful, but at least shows completeness): ``` int x; try { x = 5; f(); } catch (Exception e) { System.out.println(x); } ``` I guess the only thing missing from the definition is the finally block for completeness, and that just have to include the "exit points" of the try block (return, continue, break, and post last statement) in your "for every X" (and otherwise work like `catch(Throwable)`). -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sun Jul 14 11:52:11 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sun, 14 Jul 2024 13:52:11 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: > > For any type E extending Throwble and expression or statement X, first > define whether "X might throw E" as follows: > > - (Precise Special Exceptions) If E is assignable from one of: > ArithmeticException, ArrayIndexOutOfBoundsException, ArrayStoreException, > ClassCastException, AssertionError, ExceptionInInitializerError, > MatchException, NegativeArraySizeException, NullPointerException, (others?) > and X is one of those expressions which the JLS defines to possibly throw > that exception, then X might throw E. > - (Imprecise Special Exceptions) If E is assignable from one of: > LinkageError (or any of its defined subclasses), BootstrapMethodError, > StackOverflowError, InternalError, OutOfMemoryError, ThreadDeath (others?), > then X might throw E. > - (Checked Exceptions) If E extends neither Error nor > RuntimeException, and X is an invocation of a method that declares it > throws something assignable to E, then X might throw Ei. > - (Unchecked Exceptions) If E extends Error or RuntimeException, and X > is an invocation of a method, then X might throw E. > > Thinking a little more about this, I think the selection of the exception should be somewhat different. There are of course the exceptions that the JLS defines to be a possibility (e.g., `ArrayIndexOutOfBoundsException`), those would be good to be tracked. However, otherwise I think going down to the level of `LinkageError`, `BootstrapMethodError`, etc.is a bit too much. I think it probably would be enough to say in those cases that `Error` or any of its subclasses. Also, I don't see the point of distinguishing between checked and unchecked exceptions. Because technically it is possible for a method to throw any exception. So, as for method calls I would just say that a method call may throw any exception (maybe special casing autoboxing and string concatenation), and ignoring that possibility would be rather dangerous (especially for the DA case). Out of scope: I probably would find it better to have an independent part of the spec defining a "flow graph" for the code where this "flow graph" would be a directed graph where nodes are the atomic statements / expressions and there would be an edge (A -> B) between two nodes, iff B might be a statement / expression after A. Of course, an edge doesn't necessarily have to mean real possibility. And then the spec could refer back to this graph. For example, I think it would be easier for this (at least it would be easier from the formalization point of view). Also, if the edges would be labeled with something like "subscope", "exit scope" or something like these, then maybe it would even be reusable for the scope of pattern variables (though I haven't thought much about this). -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Sun Jul 14 17:41:57 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sun, 14 Jul 2024 12:41:57 -0500 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: On Sun, Jul 14, 2024 at 6:52?AM Attila Kelemen wrote: > I think it probably would be enough to say in those cases that `Error` or > any of its subclasses. > I agree with that... it would be simpler to just say "an Error can be thrown at any time". Also, I don't see the point of distinguishing between checked and unchecked > exceptions. Because technically it is possible for a method to throw any > exception. So, as for method calls I would just say that a method call may > throw any exception (maybe special casing autoboxing and string > concatenation), and ignoring that possibility would be rather dangerous > (especially for the DA case). > Actually this is not a problem. The worst that can happen is you end up initializing the variable twice at runtime (once legitimately in the try block, and once again in the catch block due to the unexpected exception). This is not a problem - the JVM doesn't care. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sun Jul 14 17:55:56 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sun, 14 Jul 2024 19:55:56 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: > > Also, I don't see the point of distinguishing between checked and >> unchecked exceptions. Because technically it is possible for a method to >> throw any exception. So, as for method calls I would just say that a method >> call may throw any exception (maybe special casing autoboxing and string >> concatenation), and ignoring that possibility would be rather dangerous >> (especially for the DA case). >> > > Actually this is not a problem. The worst that can happen is you end up > initializing the variable twice at runtime (once legitimately in the try > block, and once again in the catch block due to the unexpected exception). > This is not a problem - the JVM doesn't care. > I think this is the worst thing that can happen: ``` int x; try { f1(); // no checked exception declared x = 1; f2(); // may throw MyCheckedException } catch (MyCheckedException e) { System.out.println(x); } ``` According to your rules `x` is DA in the catch, and if `f1` misbehaves and throws a `MyCheckedException` then you are reading an uninitialized variable. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Sun Jul 14 18:07:53 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sun, 14 Jul 2024 13:07:53 -0500 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: On Sun, Jul 14, 2024 at 12:56?PM Attila Kelemen wrote: > Also, I don't see the point of distinguishing between checked and >>> unchecked exceptions. Because technically it is possible for a method to >>> throw any exception. So, as for method calls I would just say that a method >>> call may throw any exception (maybe special casing autoboxing and string >>> concatenation), and ignoring that possibility would be rather dangerous >>> (especially for the DA case). >>> >> >> Actually this is not a problem. The worst that can happen is you end up >> initializing the variable twice at runtime (once legitimately in the try >> block, and once again in the catch block due to the unexpected exception). >> This is not a problem - the JVM doesn't care. >> > > I think this is the worst thing that can happen: > > ``` > int x; > try { > f1(); // no checked exception declared > x = 1; > f2(); // may throw MyCheckedException > } catch (MyCheckedException e) { > System.out.println(x); > } > ``` > According to your rules `x` is DA in the catch, and if `f1` misbehaves and > throws a `MyCheckedException` then you are reading an uninitialized > variable. > Oh right - this reverse of my example could also happen. So in summary, you could initialize the variable zero, once, or two times. I still say it's not a big enough problem to worry about. If you're "going around" the JLS with tricks, some JLS guarantees are invalidated, just like with unchecked casts, etc. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Sun Jul 14 18:19:26 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Sun, 14 Jul 2024 20:19:26 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: > > I still say it's not a big enough problem to worry about. If you're "going > around" the JLS with tricks, some JLS guarantees are invalidated, just like > with unchecked casts, etc. > The reason I would worry is because there are languages like Kotlin and from its own perspective it is perfectly fine to throw any exception. And someone might want to call it from Java (not realizing the possibility). I think this is worse than unchecked casts and whatnot, because it doesn't blow up in your face. You will just get an impossible value (likely 0 or similar) which would be very hard to track down if it happens. Given that you are catching the exception, you might not even get to see the stacktrace in the logs. I think this would only be an acceptable risk if the compiler would protect you from such exceptions by wrapping the checked exception into an unchecked one. -------------- next part -------------- An HTML attachment was scrubbed... URL: From cay.horstmann at gmail.com Mon Jul 15 15:59:49 2024 From: cay.horstmann at gmail.com (Cay Horstmann) Date: Mon, 15 Jul 2024 17:59:49 +0200 Subject: JEP 477 Interacting with the Console Message-ID: I'd like to raise one more time the issue of simple console interaction. https://openjdk.org/jeps/477#Interacting-with-the-console puts up the strawman of try { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); String line = reader.readLine(); ... } catch (IOException ioe) { ... } I don't think it is convincing to cite BufferedReader. I looked up the top five intro to Java books that make up the bulk of US college market. All use Scanner, not BufferedReader. It is easy to see why: * No checked exceptions * Can read numbers Scanner has issues (mixing nextInt/nextLine, localized number input vs. non-localized print), so I could, as an author and teacher, be convinced to change to something better. The appeal of short print/println/readln methods seems obvious. But now consider number input: int age = Integer.parseInt(readln("How old are you?")); A beginning student must realize in the first week or so of instruction: * There are static and instance methods * A static method can be * qualified by a class * unqualified due to static import * one of the static methods that is magically imported * but only in a source file containing an implicit class That's not conceptually simple. If the static methods that a beginner needs in the first few weeks (including parseInt, parseDouble, exit, and a handful of the Math methods) were all unqualified, then the beginner would be helped. Otherwise, you would help the beginner more by not statically importing IO by default. They can start out with IO.println, have a consistent mental model in those first few weeks, and learn static imports later. Or, if the focus is not the beginner, but the author of small programs, the JEP could state that as the motivation. In general, the needs of beginners and authors of small programs are not the same. The JEP bounces between the needs of the two. Implicit classes are useful for both, but magic imports will add puzzlement for beginners. Cheers, Cay -- Cay S. Horstmann | https://horstmann.com From archie.cobbs at gmail.com Mon Jul 15 18:29:37 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 15 Jul 2024 13:29:37 -0500 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: On Sun, Jul 14, 2024 at 1:19?PM Attila Kelemen wrote: > I still say it's not a big enough problem to worry about. If you're "going >> around" the JLS with tricks, some JLS guarantees are invalidated, just like >> with unchecked casts, etc. >> > > The reason I would worry is because there are languages like Kotlin and > from its own perspective it is perfectly fine to throw any exception. And > someone might want to call it from Java (not realizing the possibility). I > think this is worse than unchecked casts and whatnot, because it doesn't > blow up in your face. You will just get an impossible value (likely 0 or > similar) which would be very hard to track down if it happens. Given that > you are catching the exception, you might not even get to see the > stacktrace in the logs. I think this would only be an acceptable risk if > the compiler would protect you from such exceptions by wrapping the checked > exception into an unchecked one. > Hmm.. this is starting to sound a little out of bounds. If using Kotlin creates some new danger that wasn't there before, how is that Java's problem? And in any case, wouldn't you already have a similar issue in any normal try/catch scenario with respect to Java's checked exceptions? obj.method(); // might call kotlin and throw IOException?? try { in.read(); } catch (IOException e) { // handle exception } -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Mon Jul 15 18:49:22 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Mon, 15 Jul 2024 21:49:22 +0300 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: I would sise with Atilla here, at least partially. While it's perfectly reasonable to focus on J in JDK, jdk is still a widely used platform for many languages, that are built on top of trust into respect of JVM-based languages by Java designers. Specifically in that case, I wouldn't say that this is kind of an issue that is significant enough to discard the idea, it is still a con of this change, and, imo, must be accounted. Though for kotlin specifically, there are much greater compromises that kotlin devs are used to than final variable that may be accidentally initialized twice On Mon, Jul 15, 2024, 21:30 Archie Cobbs wrote: > On Sun, Jul 14, 2024 at 1:19?PM Attila Kelemen > wrote: > >> I still say it's not a big enough problem to worry about. If you're >>> "going around" the JLS with tricks, some JLS guarantees are invalidated, >>> just like with unchecked casts, etc. >>> >> >> The reason I would worry is because there are languages like Kotlin and >> from its own perspective it is perfectly fine to throw any exception. And >> someone might want to call it from Java (not realizing the possibility). I >> think this is worse than unchecked casts and whatnot, because it doesn't >> blow up in your face. You will just get an impossible value (likely 0 or >> similar) which would be very hard to track down if it happens. Given that >> you are catching the exception, you might not even get to see the >> stacktrace in the logs. I think this would only be an acceptable risk if >> the compiler would protect you from such exceptions by wrapping the checked >> exception into an unchecked one. >> > > Hmm.. this is starting to sound a little out of bounds. If using Kotlin > creates some new danger that wasn't there before, how is that Java's > problem? > > And in any case, wouldn't you already have a similar issue in any normal > try/catch scenario with respect to Java's checked exceptions? > > obj.method(); // might call kotlin and throw IOException?? > try { > in.read(); > } catch (IOException e) { > // handle exception > } > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Mon Jul 15 18:50:08 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 15 Jul 2024 20:50:08 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: > > Hmm.. this is starting to sound a little out of bounds. If using Kotlin > creates some new danger that wasn't there before, how is that Java's > problem? > > And in any case, wouldn't you already have a similar issue in any normal > try/catch scenario with respect to Java's checked exceptions? > > obj.method(); // might call kotlin and throw IOException?? > try { > in.read(); > } catch (IOException e) { > // handle exception > } > In my opinion, anything that can be done in the byte code is Java's problem because when it links against something then it links against a bytecode. Java just has to balance on what is the consequence of ignoring something that is a possibility at bytecode level. So, the issue is different, because the consequences are different. If a method throws an undeclared checked exception, then it is awkward (and maybe annoying to some degree if you want to catch that exception) but it is generally not a big deal because almost always you just want to rethrow the exception. And as for handling a specific exception: It is probably already a futile attempt because it is unlikely that you are able to distinguish enough between the exceptions. I think a better example for a problem would have been this (I would consider this currently the worst case): ``` try { obj.method(); // might call kotlin and throw IOException?? in.read(); } catch (IOException e) { // handle exception } ``` Because in the above example, one might have been fooled into believing that it is handling a specific failure when in fact it might be something else. This can be bad but I believe (I have no statistics of course), that when people handle exceptions, then either they wrap around a single call when want to handle a specific exception, otherwise they don't really have an exact idea about the exception anyway, so they almost certainly wanted to catch that exception as well. On the other hand: A local variable taking a value that was never assigned to it (and btw. this implies a change to the memory model as well) is extremely surprising. And being extremely surprising is bad because when people are looking for the root cause they are very likely to not even think about this possibility. Making the bug hunt a lot more horrible. -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Mon Jul 15 18:51:52 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 15 Jul 2024 20:51:52 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Olexandr Rotan ezt ?rta (id?pont: 2024. j?l. 15., H, 20:49): > Specifically in that case, I wouldn't say that this is kind of an issue > that is significant enough to discard the idea, it is still a con of this > change, and, imo, must be accounted. Though for kotlin specifically, there > are much greater compromises that kotlin devs are used to than final > variable that may be accidentally initialized twice > Actually the main concern here is not that a variable is initialized twice (that would be whatever in my opinion). The main issue is that you might be able to observe a value of a variable that should be impossible. -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Mon Jul 15 21:09:56 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Tue, 16 Jul 2024 00:09:56 +0300 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Well, that's valid. However, kotlin design choice to step off from JLS in checked exceptions handling (their absence, more specifically) has its consequences, both positive and negative, and potential incompatibilities with java code in what requirements compiler imposes to final variables in context of try/catch block is one of those. While, as I said previously, we have to respect other JVM languages, if they want both bytecode compatibility with Java and step off from JLS at the same time, it has its cost. We should not break this compatibility wherever, no matter what the price is, just because we can, but if decision to change JLS brings significant value to Java itself, we certainly should do it, and it's other language teams work to bear with outcome of their decisions. While we certainly should write this issue in "cons" column, I would not mark it as crucial as long as it does not break compatibility of Java bytecode with itself. It's J in the JDK after all. On Mon, Jul 15, 2024, 21:52 Attila Kelemen wrote: > Olexandr Rotan ezt ?rta (id?pont: 2024. j?l. > 15., H, 20:49): > >> Specifically in that case, I wouldn't say that this is kind of an issue >> that is significant enough to discard the idea, it is still a con of this >> change, and, imo, must be accounted. Though for kotlin specifically, there >> are much greater compromises that kotlin devs are used to than final >> variable that may be accidentally initialized twice >> > Actually the main concern here is not that a variable is initialized twice > (that would be whatever in my opinion). The main issue is that you might be > able to observe a value of a variable that should be impossible. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jul 15 21:27:28 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 15 Jul 2024 17:27:28 -0400 Subject: JEP 477 Interacting with the Console In-Reply-To: References: Message-ID: As long as readInt and friends consume the entire line, I'm fine with that. I see no value in exit. So many of the students I helped tutor treated that like a crutch, and they had to be dragged away from it. In fact, I think I did too. That is basic control flow that should be treated as unavoidable complexity. Same as basic arithmetic and concatenation. As for Math, what's stopping us from static importing Math too? No need to reinvent the wheel if it works well enough. On Mon, Jul 15, 2024, 12:00?PM Cay Horstmann wrote: > I'd like to raise one more time the issue of simple console interaction. > > https://openjdk.org/jeps/477#Interacting-with-the-console puts up the > strawman of > > try { > BufferedReader reader = new BufferedReader(new > InputStreamReader(System.in)); > String line = reader.readLine(); > ... > } catch (IOException ioe) { > ... > } > > I don't think it is convincing to cite BufferedReader. I looked up the top > five intro to Java books that make up the bulk of US college market. All > use Scanner, not BufferedReader. It is easy to see why: > > * No checked exceptions > * Can read numbers > > Scanner has issues (mixing nextInt/nextLine, localized number input vs. > non-localized print), so I could, as an author and teacher, be convinced to > change to something better. > > The appeal of short print/println/readln methods seems obvious. But now > consider number input: > > int age = Integer.parseInt(readln("How old are you?")); > > A beginning student must realize in the first week or so of instruction: > > * There are static and instance methods > * A static method can be > * qualified by a class > * unqualified due to static import > * one of the static methods that is magically imported > * but only in a source file containing an implicit class > > That's not conceptually simple. > > If the static methods that a beginner needs in the first few weeks > (including parseInt, parseDouble, exit, and a handful of the Math methods) > were all unqualified, then the beginner would be helped. Otherwise, you > would help the beginner more by not statically importing IO by default. > They can start out with IO.println, have a consistent mental model in those > first few weeks, and learn static imports later. > > Or, if the focus is not the beginner, but the author of small programs, > the JEP could state that as the motivation. > > In general, the needs of beginners and authors of small programs are not > the same. The JEP bounces between the needs of the two. Implicit classes > are useful for both, but magic imports will add puzzlement for beginners. > > Cheers, > > Cay > > -- > > Cay S. Horstmann | https://horstmann.com > -------------- next part -------------- An HTML attachment was scrubbed... URL: From cay at horstmann.com Tue Jul 16 06:27:39 2024 From: cay at horstmann.com (Cay Horstmann) Date: Tue, 16 Jul 2024 08:27:39 +0200 Subject: JEP 477 Interacting with the Console In-Reply-To: References: Message-ID: <4b9700c2-6bbf-4105-86ca-964fb43f4c26@horstmann.com> On 15/07/2024 23.27, David Alayachew wrote: > > As for Math, what's stopping us from static importing Math too? No need to reinvent the wheel if it works well enough. > That, in a nutshell, is my point. Why have a magic rule for statically importing three methods? Minimally helpful, and puzzling for beginners. Is it worth the complexity budget? The JEP doesn't make a convincing case. Cay -- Cay S. Horstmann | https://horstmann.com From forax at univ-mlv.fr Tue Jul 16 06:41:15 2024 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 16 Jul 2024 08:41:15 +0200 (CEST) Subject: JEP 477 Interacting with the Console In-Reply-To: References: Message-ID: <1170637102.79264288.1721112075499.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "cay horstmann" > To: "amber-dev" > Sent: Monday, July 15, 2024 5:59:49 PM > Subject: JEP 477 Interacting with the Console Hello, > I'd like to raise one more time the issue of simple console interaction. > > https://openjdk.org/jeps/477#Interacting-with-the-console puts up the strawman > of > > try { > BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); > String line = reader.readLine(); > ... > } catch (IOException ioe) { > ... > } > > I don't think it is convincing to cite BufferedReader. I looked up the top five > intro to Java books that make up the bulk of US college market. All use > Scanner, not BufferedReader. It is easy to see why: > > * No checked exceptions > * Can read numbers > > Scanner has issues (mixing nextInt/nextLine, localized number input vs. > non-localized print), so I could, as an author and teacher, be convinced to > change to something better. yes, at my uni, we use to use Scanner too. > > The appeal of short print/println/readln methods seems obvious. But now consider > number input: > > int age = Integer.parseInt(readln("How old are you?")); > > A beginning student must realize in the first week or so of instruction: > > * There are static and instance methods > * A static method can be > * qualified by a class > * unqualified due to static import > * one of the static methods that is magically imported > * but only in a source file containing an implicit class > > That's not conceptually simple. > > If the static methods that a beginner needs in the first few weeks (including > parseInt, parseDouble, exit, and a handful of the Math methods) were all > unqualified, then the beginner would be helped. Otherwise, you would help the > beginner more by not statically importing IO by default. They can start out > with IO.println, have a consistent mental model in those first few weeks, and > learn static imports later. or explain after having used print, println and readln, that those are just shortcuts for IO.print, IO.println and IO.readln, after that, you can introduce Integer.parseInt. Going from println to IO.println is also the right place to introduce where to find the documentation, the online javadoc, etc. > > Or, if the focus is not the beginner, but the author of small programs, the JEP > could state that as the motivation. > > In general, the needs of beginners and authors of small programs are not the > same. The JEP bounces between the needs of the two. Implicit classes are useful > for both, but magic imports will add puzzlement for beginners. I disagree, this is only an issue if you talk about "import", seing println as a shorcut for IO.println is not a real problem. The notion of "import" in Java is tricky anyway especially because most of my students know Python so you need a full lesson about the difference between Java import and Python import, but that lesson can be taught in the second part of the semester when you need "package". > > Cheers, > > Cay regards, R?mi > > -- > > Cay S. Horstmann | https://horstmann.com From cay at horstmann.com Tue Jul 16 07:15:30 2024 From: cay at horstmann.com (Cay Horstmann) Date: Tue, 16 Jul 2024 09:15:30 +0200 Subject: JEP 477 Interacting with the Console In-Reply-To: <1170637102.79264288.1721112075499.JavaMail.zimbra@univ-eiffel.fr> References: <1170637102.79264288.1721112075499.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <16625dc0-61aa-4990-af33-a3b2f7004044@horstmann.com> On 16/07/2024 08.41, Remi Forax wrote: >> In general, the needs of beginners and authors of small programs are not the >> same. The JEP bounces between the needs of the two. Implicit classes are useful >> for both, but magic imports will add puzzlement for beginners. > > I disagree, this is only an issue if you talk about "import", seing println as a shorcut for IO.println is not a real problem. > > The notion of "import" in Java is tricky anyway especially because most of my students know Python so you need a full lesson about the difference between Java import and Python import, > but that lesson can be taught in the second part of the semester when you need "package". > Hi R?mi, I agree that not having to talk about imports right away is a win. I am all for importing java.base. I am not sure I get your point about shortcuts, though. I wouldn't want to talk about shortcuts any more than about imports. I would not want to discuss why there is a println shortcut but not parseInt or pow. Really, how painful is IO.println? (If that's too long, call it IO.out.) And it is consistent. IO.println. Integer.parseInt. Math.pow. Consistent is good for beginners. If further brevity is desired, there is an existing mechanism--static imports. Which can wait. The alternative is to go big, and to provide "shortcuts" for all beginner methods. println, parseInt, pow. Consistent and not puzzling. But of course the rub is, what are all beginner methods? Cheers, Cay -- Cay S. Horstmann | https://horstmann.com From attila.kelemen85 at gmail.com Tue Jul 16 15:32:31 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 16 Jul 2024 17:32:31 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Other languages (like Kotlin) are just examples of how it can happen the easiest. Let me explain the problem then this way (and let's ignore the more practical concerns I have raised previously): - The JLS is a big mathematical architecture, and as such has an Achilles heel: Contradiction. If there is even a single one, then everything collapses immediately. - When people wrote some sections of the JLS, there is a great likelihood that they had the assumption in their head that a variable is always assigned. For local variables, this is ensured via the "definitely assigned" property. Can you confidently say that breaking such a fundamental assumption will not cause a contradiction anywhere else in the JLS? - One option you could think of is that the JLS will now just state that if a method not declaring a checked exception throws one anyway, then that is an undefined behavior. However, if you look at the JLS, then the JLS sets a very high bar for doing so. And even then it still severely limits the possibilities. So, the problem is that the JLS cannot ignore the possibilities the JVM allows, because the JVM basically defines the laws of physics for it. If it did ignore it, then javac would be in big trouble. And not even Mourinho would want that :). That is, if the JLS states that "X" must happen, then it is never acceptable that when you run the code something else happens. Whatever you write into the JLS for this feature must cover all possibilities that might happen in the JVM (worst case by stating that doing something is an undefined behavior). Now look at the gain we might have: We could differentiate between checked and unchecked exceptions for the purpose of better flow analysis. I'm not saying it is nothing but is it really worth the risk of blowing up the JLS? Olexandr Rotan ezt ?rta (id?pont: 2024. j?l. 15., H, 23:10): > Well, that's valid. However, kotlin design choice to step off from JLS in > checked exceptions handling (their absence, more specifically) has its > consequences, both positive and negative, and potential incompatibilities > with java code in what requirements compiler imposes to final variables in > context of try/catch block is one of those. > > While, as I said previously, we have to respect other JVM languages, if > they want both bytecode compatibility with Java and step off from JLS at > the same time, it has its cost. We should not break this compatibility > wherever, no matter what the price is, just because we can, but if decision > to change JLS brings significant value to Java itself, we certainly should > do it, and it's other language teams work to bear with outcome of their > decisions. > > While we certainly should write this issue in "cons" column, I would not > mark it as crucial as long as it does not break compatibility of Java > bytecode with itself. It's J in the JDK after all. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Jul 16 15:45:54 2024 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 16 Jul 2024 17:45:54 +0200 (CEST) Subject: JEP 477 Interacting with the Console In-Reply-To: <16625dc0-61aa-4990-af33-a3b2f7004044@horstmann.com> References: <1170637102.79264288.1721112075499.JavaMail.zimbra@univ-eiffel.fr> <16625dc0-61aa-4990-af33-a3b2f7004044@horstmann.com> Message-ID: <1321090.81484515.1721144754916.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "Cay Horstmann" > To: "Remi Forax" > Cc: "amber-dev" > Sent: Tuesday, July 16, 2024 9:15:30 AM > Subject: Re: JEP 477 Interacting with the Console > On 16/07/2024 08.41, Remi Forax wrote: >>> In general, the needs of beginners and authors of small programs are not the >>> same. The JEP bounces between the needs of the two. Implicit classes are useful >>> for both, but magic imports will add puzzlement for beginners. >> >> I disagree, this is only an issue if you talk about "import", seing println as a >> shorcut for IO.println is not a real problem. >> >> The notion of "import" in Java is tricky anyway especially because most of my >> students know Python so you need a full lesson about the difference between >> Java import and Python import, >> but that lesson can be taught in the second part of the semester when you need >> "package". >> > > Hi R?mi, > > I agree that not having to talk about imports right away is a win. I am all for > importing java.base. > > I am not sure I get your point about shortcuts, though. I wouldn't want to talk > about shortcuts any more than about imports. I would not want to discuss why > there is a println shortcut but not parseInt or pow. Shorcuts is not the right term, convenient is a better term. This is the word we use to explain "var" too. > > Really, how painful is IO.println? (If that's too long, call it IO.out.) And it > is consistent. IO.println. Integer.parseInt. Math.pow. Consistent is good for > beginners. If further brevity is desired, there is an existing > mechanism--static imports. Which can wait. I think this is perfectly fine to not use "println" but "IO.println" instead and mention later that it can be abbreviated to "println". > > The alternative is to go big, and to provide "shortcuts" for all beginner > methods. println, parseInt, pow. Consistent and not puzzling. But of course the > rub is, what are all beginner methods ? and the fact that all these names polute the scope. We have exercises where students rewrite simple functions like pow or parseInt, it would be sad if IO.parseInt is called instead of the method "parseint" defined by a student because the method call is written "parseInt(...)". > > Cheers, > > Cay regards, R?mi > > -- > > Cay S. Horstmann | https://horstmann.com From archie.cobbs at gmail.com Tue Jul 16 16:57:31 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 16 Jul 2024 11:57:31 -0500 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: On Tue, Jul 16, 2024 at 10:32?AM Attila Kelemen wrote: > Now look at the gain we might have: We could differentiate between checked > and unchecked exceptions for the purpose of better flow analysis. I'm not > saying it is nothing but is it really worth the risk of blowing up the JLS? > I agree with you that relying on properly checked exceptions opens up a new kind of hole that didn't exist before. In other words, in the places that it doesn't perfectly match reality, the current DA logic always errs on the side of caution, but if it started relying on properly checked exceptions that would mean doing the opposite, even though (arguably) it has the "right" to do so. OK so for the sake of argument let's take your position and suppose we don't want to do that. Then every method call becomes a possible branch to the catch clause (regardless of what is caught). In addition, you still have all the special cases like NegativeArraySizeException, and you have Error being thrown anywhere. So now basically everything except "trivial stuff" like plain arithmetic must be assumed to possibly throw some kind of exception. And all this buys beyond the original simpler proposal is that the compiler now knows that "trailing trivial stuff" can't throw an exception. For example: final int x; int neg_x = 0; try { x = method(); neg_x = -x; // "trailing trivial stuff" } catch (Exception e) { x = 0; } At this point, is it even worth it to do this more complicated analysis? It's not much gain for all that work when compared to the original simpler fix. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From cay at horstmann.com Tue Jul 16 16:59:01 2024 From: cay at horstmann.com (Cay Horstmann) Date: Tue, 16 Jul 2024 18:59:01 +0200 Subject: JEP 477 Interacting with the Console In-Reply-To: <1321090.81484515.1721144754916.JavaMail.zimbra@univ-eiffel.fr> References: <1170637102.79264288.1721112075499.JavaMail.zimbra@univ-eiffel.fr> <16625dc0-61aa-4990-af33-a3b2f7004044@horstmann.com> <1321090.81484515.1721144754916.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <39897475-1ec2-45b3-80ff-134e2c771424@horstmann.com> On 16/07/2024 17.45, forax at univ-mlv.fr wrote: > I think this is perfectly fine to not use "println" but "IO.println" instead and mention later that it can be abbreviated to "println". Like I said, the JEP alternates between two separate aims for two different audiences: brevity for scripters, and clarity for beginners. If the JEP says "scripters demand brief println, print, readln, and we've decided to give it to them", then, sure. In JEP 430, STR was a magic import. But that isn't what the JEP says. It claims this as a benefit for beginners, which is not plausible. Introducing special brief println, print, readln is likely to increase puzzlement with beginners. That's the question I wish the JEP would address. Has this tradeoff been considered and deemed worthwhile? Cheers, Cay -- Cay S. Horstmann | https://horstmann.com From ethan at mccue.dev Tue Jul 16 17:11:49 2024 From: ethan at mccue.dev (Ethan McCue) Date: Tue, 16 Jul 2024 13:11:49 -0400 Subject: JEP 477 Interacting with the Console In-Reply-To: <1321090.81484515.1721144754916.JavaMail.zimbra@univ-eiffel.fr> References: <1170637102.79264288.1721112075499.JavaMail.zimbra@univ-eiffel.fr> <16625dc0-61aa-4990-af33-a3b2f7004044@horstmann.com> <1321090.81484515.1721144754916.JavaMail.zimbra@univ-eiffel.fr> Message-ID: I still am not for importing java.base, for the reasons I think I talked to death in that other thread. I guess while we are on the topic, what is the rationale behind naming the method println? The current draft on github has println, print, and readln. But printing isn't the inverse of reading - writing is the inverse of reading. I agree that the metaphor of "printing to the screen" is used widely, but I don't see why we would pick that given a blank slate. Then also readln over readLine strikes me as a strange tradeoff. That's the kind of thing that helps someone who already knows that "ln" is an abbreviation for "line" write code - i.e. the group Cay identified as "authors of small programs." For beginners it's one more non-obvious thing that needs to be explained. On Tue, Jul 16, 2024 at 12:43?PM wrote: > ----- Original Message ----- > > From: "Cay Horstmann" > > To: "Remi Forax" > > Cc: "amber-dev" > > Sent: Tuesday, July 16, 2024 9:15:30 AM > > Subject: Re: JEP 477 Interacting with the Console > > > On 16/07/2024 08.41, Remi Forax wrote: > >>> In general, the needs of beginners and authors of small programs are > not the > >>> same. The JEP bounces between the needs of the two. Implicit classes > are useful > >>> for both, but magic imports will add puzzlement for beginners. > >> > >> I disagree, this is only an issue if you talk about "import", seing > println as a > >> shorcut for IO.println is not a real problem. > >> > >> The notion of "import" in Java is tricky anyway especially because most > of my > >> students know Python so you need a full lesson about the difference > between > >> Java import and Python import, > >> but that lesson can be taught in the second part of the semester when > you need > >> "package". > >> > > > > Hi R?mi, > > > > I agree that not having to talk about imports right away is a win. I am > all for > > importing java.base. > > > > I am not sure I get your point about shortcuts, though. I wouldn't want > to talk > > about shortcuts any more than about imports. I would not want to discuss > why > > there is a println shortcut but not parseInt or pow. > > Shorcuts is not the right term, convenient is a better term. > This is the word we use to explain "var" too. > > > > > Really, how painful is IO.println? (If that's too long, call it IO.out.) > And it > > is consistent. IO.println. Integer.parseInt. Math.pow. Consistent is > good for > > beginners. If further brevity is desired, there is an existing > > mechanism--static imports. Which can wait. > > I think this is perfectly fine to not use "println" but "IO.println" > instead and mention later that it can be abbreviated to "println". > > > > > The alternative is to go big, and to provide "shortcuts" for all beginner > > methods. println, parseInt, pow. Consistent and not puzzling. But of > course the > > rub is, what are all beginner methods ? > > and the fact that all these names polute the scope. > > We have exercises where students rewrite simple functions like pow or > parseInt, it would be sad if IO.parseInt is called instead of the method > "parseint" defined by a student because the method call is written > "parseInt(...)". > > > > > Cheers, > > > > Cay > > regards, > R?mi > > > > > -- > > > > Cay S. Horstmann | https://horstmann.com > -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Tue Jul 16 17:18:18 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Tue, 16 Jul 2024 20:18:18 +0300 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: I would say that even defining some "trivial stuff" is a complexity itself, so we either should opt into analysis or opt out of it completely On Tue, Jul 16, 2024 at 7:57?PM Archie Cobbs wrote: > On Tue, Jul 16, 2024 at 10:32?AM Attila Kelemen < > attila.kelemen85 at gmail.com> wrote: > >> Now look at the gain we might have: We could differentiate between >> checked and unchecked exceptions for the purpose of better flow analysis. >> I'm not saying it is nothing but is it really worth the risk of blowing up >> the JLS? >> > > I agree with you that relying on properly checked exceptions opens up a > new kind of hole that didn't exist before. In other words, in the places > that it doesn't perfectly match reality, the current DA logic always errs > on the side of caution, but if it started relying on properly checked > exceptions that would mean doing the opposite, even though (arguably) it > has the "right" to do so. > > OK so for the sake of argument let's take your position and suppose we > don't want to do that. Then every method call becomes a possible branch to > the catch clause (regardless of what is caught). In addition, you still > have all the special cases like NegativeArraySizeException, and you have > Error being thrown anywhere. > > So now basically everything except "trivial stuff" like plain arithmetic > must be assumed to possibly throw some kind of exception. And all this buys > beyond the original simpler proposal is that the compiler now knows that > "trailing trivial stuff" can't throw an exception. For example: > > final int x; > int neg_x = 0; > try { > x = method(); > neg_x = -x; // "trailing trivial stuff" > } catch (Exception e) { > x = 0; > } > > At this point, is it even worth it to do this more complicated analysis? > It's not much gain for all that work when compared to the original simpler > fix. > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Tue Jul 16 17:34:57 2024 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 16 Jul 2024 19:34:57 +0200 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: > > final int x; > int neg_x = 0; > try { > x = method(); > neg_x = -x; // "trailing trivial stuff" > } catch (Exception e) { > x = 0; > } > > At this point, is it even worth it to do this more complicated analysis? > It's not much gain for all that work when compared to the original simpler > fix. > If it is worth it or not of course is a question of preference. To my mind, it is much cleaner than the special rule for "last statement is assignment". I think the only way to decide if it is worth it or not is if many more people would voice their opinion. To me it just feels too special to talk about the "last assignment" and what is that last statement. To me actually the most strange property of the "last assignment" rule is that an "if-else" chain would not work. Not so much that I want to make one more trivial assignment. But I think if the rule wants to allow the "if-else" chain then it is probably easier to allow your last example as well. As for the exceptions: There can be other middle grounds here to simplify stuff. I originally thought that a statement or expression can either throw an exception or not, and if it can, we assume that it may throw literally anything (so, `x = a.b;` is assumed to potentially throw an `IOException`). That would make the rules a lot simpler, yet would be still less surprising in my opinion. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Jul 16 17:57:23 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 16 Jul 2024 12:57:23 -0500 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: OK so just to summarize the various options... A. "Last assignment" - the original simple fix that only applies when the last statement is an assignment statement B. "Simple throws" - classify all statements in two buckets "can throw" and "cannot throw"; assume any "can throw" statement can throw anything type of exception C. "Complex throws, any checked" - classify all statements by which exception types they can throw; assume methods can throw any unchecked or checked exceptions D. "Complex throws, declared checked" - classify all statements by which exception types they can throw; assume methods can throw any unchecked or declared checked exceptions And of course "do nothing" is always the default option :) I'd also like to hear other folks' opinions. -Archie On Tue, Jul 16, 2024 at 12:35?PM Attila Kelemen wrote: > final int x; >> int neg_x = 0; >> try { >> x = method(); >> neg_x = -x; // "trailing trivial stuff" >> } catch (Exception e) { >> x = 0; >> } >> >> At this point, is it even worth it to do this more complicated analysis? >> It's not much gain for all that work when compared to the original simpler >> fix. >> > > If it is worth it or not of course is a question of preference. To my > mind, it is much cleaner than the special rule for "last statement is > assignment". I think the only way to decide if it is worth it or not is if > many more people would voice their opinion. To me it just feels too special > to talk about the "last assignment" and what is that last statement. > > To me actually the most strange property of the "last assignment" rule is > that an "if-else" chain would not work. Not so much that I want to make one > more trivial assignment. But I think if the rule wants to allow the > "if-else" chain then it is probably easier to allow your last example as > well. > > As for the exceptions: There can be other middle grounds here to simplify > stuff. I originally thought that a statement or expression can either throw > an exception or not, and if it can, we assume that it may throw literally > anything (so, `x = a.b;` is assumed to potentially throw an `IOException`). > That would make the rules a lot simpler, yet would be still less surprising > in my opinion. > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Wed Jul 17 15:15:06 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Wed, 17 Jul 2024 16:15:06 +0100 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: Hi, I've been following this thread from afar. To be honest, the cost in terms of spec complexity don't seem to be worth the increased expressiveness. (A) seems simple enough, but I tend to agree that it does look a bit ad-hoc, and will break when more complex control flow is involved. With (C) and (D) we seem to hit diminishing returns quickly (do we really want to know which assignments are or aren't possible given the type of exception thrown by statement and the catch we're in - seems overkill). (B) seems the best compromise, in the sense that at least there's only a predicate on statement. But DA/DU is already a complex beast as it is, and I just don't see enough of a justification to "fix" this. Of course this subjective. Maurizio On 16/07/2024 18:57, Archie Cobbs wrote: > OK so just to summarize the various options... > > A. "Last assignment" - the original simple fix that only applies when > the last statement is an assignment statement > B. "Simple throws" - classify all statements in two buckets "can > throw" and "cannot throw"; assume any "can throw" statement can throw > anything type of exception > C. "Complex throws, any checked" - classify all statements by which > exception types they can throw; assume methods can throw any unchecked > or checked exceptions > D. "Complex throws, declared checked" - classify all statements by > which exception types they can throw; assume methods can throw any > unchecked or declared checked exceptions > > And of course "do nothing" is always the default option :) > > I'd also like to hear other folks' opinions. > > -Archie > > On Tue, Jul 16, 2024 at 12:35?PM Attila Kelemen > wrote: > > final int x; > int neg_x = 0; > try { > ??? x = method(); > ??? neg_x = -x; ?? // "trailing trivial stuff" > } catch (Exception e) { > ??? x = 0; > } > > At this point, is it even worth it to do this more complicated > analysis? It's not much gain for all that work when compared > to the original simpler fix. > > > If it is worth it or not of course is a question of preference. To > my mind, it is much cleaner than the special rule for "last > statement is assignment". I think the only way to decide if it is > worth it or not is if many more people would voice their opinion. > To me it just feels too special to talk about the "last > assignment" and what is that last statement. > > To me actually the most strange property of the "last assignment" > rule is that an "if-else" chain would not work. Not so much that I > want to make one more trivial assignment. But I think if the rule > wants to allow the "if-else" chain then it is probably easier to > allow your last example as well. > > As for the exceptions: There can be other middle grounds here to > simplify stuff. I originally thought that a statement or > expression can either throw an exception or not, and if it can, we > assume that it may throw literally anything (so, `x = a.b;` is > assumed to potentially throw an `IOException`). That would make > the rules a lot simpler, yet would be still less surprising in my > opinion. > > > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Jul 23 14:16:53 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 23 Jul 2024 10:16:53 -0400 Subject: Try/Catch DU Exception In-Reply-To: References: Message-ID: On Wed, Jul 17, 2024 at 11:15?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > I've been following this thread from afar. To be honest, the cost in terms > of spec complexity don't seem to be worth the increased expressiveness. > I can't really disagree with that assessment. This makes for an interesting case study in the limits of amber style language tweaks. That is, it's a shame it seems, in this case at least, we can't have all three of: (a) small improvement to developer experience, (b) small change to specification, and (c) non-hacky change to specification. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jul 29 02:16:36 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 28 Jul 2024 22:16:36 -0400 Subject: JEP 477 Interacting with the Console In-Reply-To: References: <1170637102.79264288.1721112075499.JavaMail.zimbra@univ-eiffel.fr> <16625dc0-61aa-4990-af33-a3b2f7004044@horstmann.com> <1321090.81484515.1721144754916.JavaMail.zimbra@univ-eiffel.fr> Message-ID: The rationale should be obvious: System.out.println is not going away, nor are the several billion uses of println in existing Java code.? Were we to choose another name "because its better", then people have to learn two names, and learn that they do the same thing.? In this, we chose familarity / minimal hamming distance over finding the "best" name as if it were a clean-slate exercise (because it is not a clean slate exercise.) (I'll also point out that had we done as you suggest, mirror-universe Ethan would be asking the exact same question (e.g., "what is the rationale behind picking something that is gratuitously different from how it is done today")) On 7/16/2024 1:11 PM, Ethan McCue wrote: > I still am not for importing java.base, for the reasons I think I > talked to death in that other thread. > > I guess while we are on the topic, what is the rationale behind naming > the method println? > > The current draft on github has println, print, and readln. But > printing isn't the inverse of reading - writing is the inverse of > reading. I agree that the metaphor of "printing to the screen" is used > widely, but I don't see why we would pick that given a blank slate. > > Then also readln?over readLine?strikes me as a strange tradeoff. > That's the kind of thing that helps someone who already knows that > "ln" is an abbreviation for "line" write code - i.e. the group Cay > identified as "authors of small programs." For beginners it's one more > non-obvious thing that needs to be explained. > > On Tue, Jul 16, 2024 at 12:43?PM wrote: > > ----- Original Message ----- > > From: "Cay Horstmann" > > To: "Remi Forax" > > Cc: "amber-dev" > > Sent: Tuesday, July 16, 2024 9:15:30 AM > > Subject: Re: JEP 477 Interacting with the Console > > > On 16/07/2024 08.41, Remi Forax wrote: > >>> In general, the needs of beginners and authors of small > programs are not the > >>> same. The JEP bounces between the needs of the two. Implicit > classes are useful > >>> for both, but magic imports will add puzzlement for beginners. > >> > >> I disagree, this is only an issue if you talk about "import", > seing println as a > >> shorcut for IO.println is not a real problem. > >> > >> The notion of "import" in Java is tricky anyway especially > because most of my > >> students know Python so you need a full lesson about the > difference between > >> Java import and Python import, > >> but that lesson can be taught in the second part of the > semester when you need > >> "package". > >> > > > > Hi R?mi, > > > > I agree that not having to talk about imports right away is a > win. I am all for > > importing java.base. > > > > I am not sure I get your point about shortcuts, though. I > wouldn't want to talk > > about shortcuts any more than about imports. I would not want to > discuss why > > there is a println shortcut but not parseInt or pow. > > Shorcuts is not the right term, convenient is a better term. > This is the word we use to explain "var" too. > > > > > Really, how painful is IO.println? (If that's too long, call it > IO.out.) And it > > is consistent. IO.println. Integer.parseInt. Math.pow. > Consistent is good for > > beginners. If further brevity is desired, there is an existing > > mechanism--static imports. Which can wait. > > I think this is perfectly fine to not use "println" but > "IO.println" instead and mention later that it can be abbreviated > to "println". > > > > > The alternative is to go big, and to provide "shortcuts" for all > beginner > > methods. println, parseInt, pow. Consistent and not puzzling. > But of course the > > rub is, what are all beginner methods ? > > and the fact that all these names polute the scope. > > We have exercises where students rewrite simple functions like pow > or parseInt, it would be sad if IO.parseInt is called instead of > the method "parseint" defined by a student because the method call > is written "parseInt(...)". > > > > > Cheers, > > > > Cay > > regards, > R?mi > > > > > -- > > > > Cay S. Horstmann | https://horstmann.com > -------------- next part -------------- An HTML attachment was scrubbed... URL: