From tzengshinfu at gmail.com Mon Sep 4 03:14:04 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Mon, 4 Sep 2023 11:14:04 +0800 Subject: Idea[Simplified try-with-resources statement] Message-ID: Hi, Folks: Recently, I've been using `JBang` to write some script tools, and I feel that Java has transformed with modern syntax features like `Records`, `Switch Expressions`, and `Pattern Matching`. These newly added language features provide developers with more options in specific scenarios, almost like giving Java a set of wings. When using the `try-with-resources statement`, I thought it might be possible to introduce an additional simplified syntax, which I believe would be useful when developing simple, small-scale programs. Meanwhile, scenarios requiring resource closure timing specification or exception handling can still make use of the original syntax. At first glance, this simplified syntax may seem a bit unusual, but considering that `switch` itself encompasses both statements and expressions, and that `switch expression`[https://openjdk.org/jeps/361] also adds a semicolon at the end of `}`, perhaps it won't be as difficult to accept. Because it reduces indentation, making the logic clearer and more visible, users don't need to consider resource closure timing; they can focus solely on business logic. It's possible that in the future, the likelihood of using this simplified syntax will increase. In my personal opinion, the original `try-with-resources statement` feels like driving a manual transmission car, where I have full control over everything. However, if we can let the runtime manage it autonomously, it's like driving an automatic car on the long journey of coding, and even more effortless. If there could be an alternative approach that is both hassle-free and doesn't require altering Java specifications, I hope that experienced individuals can share their insights. Thanks for your interest and support, Hsinfu Tseng (Using the JEP template because I've found it helps clarify my thoughts.) ## Summary Allow omitting the definition of scope when declaring `try-with-resources statement`. ## Goals * Not introducing new scopes to reduce the cognitive burden on developers, especially beginners. * Reducing the interference of hierarchical indentation to enhance code readability. * The declaration position of variables is not restricted by `try-with-resources statement`. * Ensure that resources are released before the current scope ends. ## Motivation The `try-with-resources statement` simplified resource management in Java after version 7. However, its side effect is the necessity to declare a scope and, at the same time, it restricts lifetime of the variable declared within the scope: ```java public class example1 { public static void main(String... args) throws FileNotFoundException, IOException { try (BufferedReader reader = new BufferedReader(new FileReader("studentName.txt"))) { String studentName = "not found"; /* Variable 'studentName' is only alive in the scope of try-with-resources statement. */ while ((studentName = reader.readLine()) != null) { break; } } System.out.println(studentName); /* Variable 'studentName' cannot be resolved here. */ } } ``` While it's possible to move variable declaration before `try-with-resources statement` and modify variable content within the scope of `try-with-resources statement`, the actions of declaration, assignment, and usage of variables are segmented by the scope of `try-with-resources statement`. In practice, we would prefer variable declarations to be as close as possible to their usage locations and avoid unnecessary separation[https://rules.sonarsource.com/java/RSPEC-1941/]: ```java public class example2 { public static void main(String... args) throws FileNotFoundException, IOException { String studentName = "not found"; // #region // The scope of try-with-resources statement separates // the declaration, assignment, and usage of variable 'studentName'. try (BufferedReader reader = new BufferedReader(new FileReader("studentName.txt"))) { while ((studentName = reader.readLine()) != null) { break; } } // #endregion System.out.println(studentName); } } ``` Furthermore, methods that involve multiple resources requiring management and dependencies might become cumbersome in terms of syntax due to `nested try-with-resources statements`: ```java public class example3 { public static void main(String... args) throws SQLException { String jdbcUrl = "jdbcUrl"; String username = "username"; String password = "password"; try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) { String selectSQL = "selectSQL"; String studentID = "studentID"; try (PreparedStatement statement = conn.prepareStatement(selectSQL)) { statement.setString(1, studentID); try (ResultSet result = statement.executeQuery()) { String studentName = "not found"; while (result.next()) { studentName = result.getString(1); } System.out.println(studentName); } } } } } ``` This proposal introduces a shorthand declaration that eliminates the scope limitations of `try-with-resources statement`. It enables variable declaration, assignment, and usage within the same scope, reduces indentation levels for improved code readability (particularly in `nested try-with-resources statements`), and defers the timing of resource release to the system, ensuring resource disposal before the scope in `try-with-resources statement` is declared ends. (Similar to Golang's `Defer statement` + resource close[https://go.dev/tour/flowcontrol/12], C#'s `using declarations`[https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using], and Swift's `Defer statement` + resource close[https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement]) ## Description The format of the `simplified try-with-resources statement` is as follows: ```java try (resources);/* Change '{}' to ';'. */ ``` It can be observed that omitting `{}` reduces the indentation, thus enhancing code readability by not introducing new scopes. Moreover, variable declarations can be positioned either before or after the `simplified try-with-resources statement`: ```java public class example1_simplified { public static void main(String... args) throws FileNotFoundException, IOException { try (BufferedReader reader = new BufferedReader(new FileReader("studentName.txt"))); String studentName = "not found"; while ((studentName = reader.readLine()) != null) { break; } System.out.println(studentName); } } ``` Because it automatically converts the `simplified try-with-resources statement` to an equivalent `try-with-resources statement` after compilation: ```java public class example1_converted { public static void main(String... args) throws FileNotFoundException, IOException { try (BufferedReader reader = new BufferedReader(new FileReader("studentName.txt"))) {/* Change ';' back to '{'. */ String studentName = "not found"; while ((studentName = reader.readLine()) != null) { break; } System.out.println(studentName); }/* Add '}' before the current scope ends. */ } } ``` Because the scope of `simplified try-with-resources statement` extends until the current scope is terminated, the variable `studentName`, which originally couldn't be resolved in `example1`, can now be resolved successfully. This allows variable declaration, assignment, and usage within the same scope, ensuring that resource close operations occur before the current scope ends. Invoking multiple dependent `simplified try-with-resources statements` is similar to regular variable declarations, thereby avoiding excessive nesting. ```java public class example3_simplified { public static void main(String... args) throws SQLException { String jdbcUrl = "jdbcUrl"; String username = "username"; String password = "password"; try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)); String selectSQL = "selectSQL"; String studentID = "studentID"; try (PreparedStatement statement = conn.prepareStatement(selectSQL)); statement.setString(1, studentID); try (ResultSet result = statement.executeQuery()); String studentName = "not found"; while (result.next()) { studentName = result.getString(1); } System.out.println(studentName); } } ``` It will be converted to equivalent `nested try-with-resources statements` after compilation: ```java public class example3_converted { public static void main(String... args) throws SQLException { String jdbcUrl = "jdbcUrl"; String username = "username"; String password = "password"; try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {/* Change ';' back to '{'. */ String selectSQL = "selectSQL"; String studentID = "studentID"; try (PreparedStatement statement = conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */ statement.setString(1, studentID); try (ResultSet result = statement.executeQuery()) {/* Change ';' back to '{'. */ String studentName = "not found"; while (result.next()) { studentName = result.getString(1); } System.out.println(studentName); }}}}/* Add the corresponding number (in this case, 3) of '}' before the current scope ends. */ } ``` After code formatting: ```java public class example3_formatted { public static void main(String... args) throws SQLException { String jdbcUrl = "jdbcUrl"; String username = "username"; String password = "password"; try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) { String selectSQL = "selectSQL"; String studentID = "studentID"; try (PreparedStatement statement = conn.prepareStatement(selectSQL)) { statement.setString(1, studentID); try (ResultSet result = statement.executeQuery()) { String studentName = "not found"; while (result.next()) { studentName = result.getString(1); } System.out.println(studentName); } } } } } ``` Comparing the code structures of `example3_simplified` and `example3_formatted`, it is evident that the usage of `simplified try-with-resources statement` enhances code readability by significantly reducing nested indentation levels. Additionally, it can be observed that in the converted `nested try-with-resources statements`, resource close actions will be executed in reverse order of declaration, just before the current scope ends. If coupled with `JEP 445: Unnamed Classes and Instance Main Methods (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to create a simple, small-scale program: ```java String jdbcUrl = "jdbcUrl"; String username = "username"; String password = "password"; String selectSQL = "selectSQL"; String studentID = "studentID"; void main() throws SQLException { try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)); try (PreparedStatement statement = conn.prepareStatement(selectSQL)); statement.setString(1, studentID); try (ResultSet result = statement.executeQuery()); String studentName = "not found"; while (result.next()) { studentName = result.getString(1); } System.out.println(studentName); } ``` ## Alternatives * One approach to mitigate the impact on readability caused by deeply `nested try-with-resources statements` is to consolidate multiple resources within the same `try-with-resources statement` as much as possible. However, formatting multiple resources with line breaks can also disrupt visual clarity, and furthermore, it's not always feasible to close many resources simultaneously: ```java public class alternative1 { public static void main(String... args) throws SQLException { String jdbcUrl = "jdbcUrl"; String username = "username"; String password = "password"; String selectSQL = "selectSQL"; String studentID = "studentID"; try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password); PreparedStatement statement = conn.prepareStatement(selectSQL)) { /* In this case, there are only 2 resources, but if there are 3 or more, it will become even less aesthetically pleasing. */ statement.setString(1, studentID); try (ResultSet result = statement.executeQuery()) { /* ResultSet 'result' cannot be closed simultaneously with PreparedStatement 'statement' because PreparedStatement 'statement' needs parameter configuration first. */ String studentName = "not found"; while (result.next()) { studentName = result.getString(1); } return studentName; } } } } ``` * Alternatively, creating a separate method to confine the scope of managed resources within the method can be done, but it necessitates 'naming' the new method[https://www.karlton.org/2017/12/naming-things-hard/]. This approach simply shifts complexity to the interior of additional method, which may not be conducive to creating simple, small-scale programs: ```java public class alternative2 { public static void main(String... args) throws SQLException { String selectSQL = "selectSQL"; String studentID = "studentID"; String studentName = getStudentName(selectSQL, studentID); System.out.println(studentName); } public static String getStudentName(String sql, String studentID) throws SQLException { String jdbcUrl = "jdbcUrl"; String username = "username"; String password = "password"; try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) { try (PreparedStatement statement = conn.prepareStatement(sql)) { statement.setString(1, studentID); try (ResultSet result = statement.executeQuery()) { String studentName = "not found"; while (result.next()) { studentName = result.getString(1); } return studentName; } } } } } ``` /* GET BETTER EVERY DAY */ From arne.styve at ntnu.no Fri Sep 15 15:08:04 2023 From: arne.styve at ntnu.no (Arne Styve) Date: Fri, 15 Sep 2023 15:08:04 +0000 Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move forward! Message-ID: I recently came across this JEP, and was very surprised. Why on earth should you add features to a professional programming language just to apparently make it a bit easier for students and beginners to learn to code? And especially when the suggested feature has no real use (and cannot be used) once you learn to program OOP properly? I?ve been a SW-developer and manager for more than 20 years, and a teacher of OOP at University for 15 years, and have never, ever experienced that the public static void main(String[] args) have been remotely problematic for students in learning to program. Please have this JEP terminated. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Sep 15 15:17:53 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 15 Sep 2023 11:17:53 -0400 Subject: String Templates Expression Statement In-Reply-To: References: Message-ID: <35f63518-eb8d-6db1-8745-2ea80641c216@oracle.com> We have gone back and forth on this, but I believe we are better served by not allowing string templates as statement expressions.? A string template is supposed to _produce something_, not _operate by side effects_. A statement expression serves only to normalize its use to produce side effects. On 7/12/2023 7:26 AM, Bas Leijdekkers wrote: > Hello Amber-dev, > > In the String Templates draft specification, I don't see any changes > in JLS Chapter 14.8 Expression Statements. This makes me think that > Template Expressions are not allowed to be used as statements. However > javac in jdk21-ea accepts a Template Expression used as a statement > without reporting any error. (I am using?build 21-ea+30-2426). So it > appears to me either the specification or javac is incorrect. Which > one should I believe? > > Example code: > ``` > class TemplateExpressionStatement { > ? ? public static void main(String[] args) { > ? ? ? ? STR."\{}"; > ? ? } > } > ``` > > Bas -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Fri Sep 15 16:09:10 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 15 Sep 2023 18:09:10 +0200 (CEST) Subject: String Templates Expression Statement In-Reply-To: <35f63518-eb8d-6db1-8745-2ea80641c216@oracle.com> References: <35f63518-eb8d-6db1-8745-2ea80641c216@oracle.com> Message-ID: <1136052418.39573496.1694794149996.JavaMail.zimbra@univ-eiffel.fr> Given this is a spec issue, I think it should be discussed on the spec-experts list, > From: "Brian Goetz" > To: "Bas Leijdekkers" , "amber-dev" > > Sent: Friday, September 15, 2023 5:17:53 PM > Subject: Re: String Templates Expression Statement > We have gone back and forth on this, but I believe we are better served by not > allowing string templates as statement expressions. A string template is > supposed to _produce something_, not _operate by side effects_. A statement > expression serves only to normalize its use to produce side effects. A string template is a value, it does not produce anything. So I suppose you mean a String template "processor" is supposed to produce something. And for the record, the reason we are giong back and forth is that restricting the syntax to only support expression does not fit well with the fact that a processor is specified as an interface. So we are allowing the definition of a processor to have states but we are disallowing the syntax that calls a processor to modify those states. Personnally, I can leave with that, the Java semantics is full of this kind of special cases. And if someone really want a processor to acts as builder in the generic sense, the syntax var _ = processor."..."; works anyway. R?mi > On 7/12/2023 7:26 AM, Bas Leijdekkers wrote: >> Hello Amber-dev, >> In the String Templates draft specification, I don't see any changes in JLS >> Chapter 14.8 Expression Statements. This makes me think that Template >> Expressions are not allowed to be used as statements. However javac in jdk21-ea >> accepts a Template Expression used as a statement without reporting any error. >> (I am using build 21-ea+30-2426). So it appears to me either the specification >> or javac is incorrect. Which one should I believe? >> Example code: >> ``` >> class TemplateExpressionStatement { >> public static void main(String[] args) { >> STR."\{}"; >> } >> } >> ``` >> Bas -------------- next part -------------- An HTML attachment was scrubbed... URL: From talden at gmail.com Sat Sep 16 00:53:44 2023 From: talden at gmail.com (Aaron Scott-Boddendijk) Date: Sat, 16 Sep 2023 12:53:44 +1200 Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move forward! In-Reply-To: References: Message-ID: To provide another perspective that doesn't come from anyone inside the JDK development process or affiliated with any organisation that is. I have been in professional software development for 30 years. While I am not involved formally in education, I do 'give back' by involving myself in several active Java communities. For students new to programming, this is a mix of self-guided individuals (or misleadingly guided - youtube is filled with some terrible material), or lost-at-sea secondary and tertiary course students (you may not see all those in your courses that are struggling, and may not be the sole source of education on the topic). I have also followed, in depth, the initial discussions of the features in this JEP from early inception, through the JEP's evolution to its current preview state. There are two things this JEP addresses. One is directly for the student, shrinking their learning load, and the second is one of mind-share. If the initial experience is not simplified then students with a choice will engage with other languages first and educators will increasingly pivot away from Java to languages that present an easier first experience. That initial experience is filled with complexities such as the static/non-static distinction, the ceremonies of declaring a class (even the minor matter of matching, case-sensitively, the public class name to the filename) or, as your posting focuses on, the complexity of the entry-point declaration. Oft-used mantras such as "just ignore this bit for now" or "just copy this and ignore the stuff above and below" don't truly lift the cognitive burden from the students as well as we'd hope. With the frequency of students struggling with the initial experience, educators have to accept that it is human nature to either dwell on these details or be impacted by the distraction of reading around them. Removing the need for them does have some merit to early learning. I should note that I was initially negative towards this JEP myself. I had concerns that it was attempting to overfit the initial engagement experience to the detriment of later learning progression. This is seen often in libraries and frameworks that tune a part of their API to present a more elegant, but eventually irrelevant, hello-world case - an increasingly common "please pick me" marketing ploy for a system component that should be selected on long-term merits, not initial gut-feelings. However, having been involved (largely as an observer) in the evolution of these changes, I can say that the discussion has been robust. Several proposals were rejected or revisited specifically because, while they would make an even simpler first step, they would add new burdens to progression. I no longer feel that this is an irresponsible or detracting change. As a developer with some projects heavily invested in Java, it is good to see that Oracle and others are investing significant effort in a large number of very high-value features (the JVM Language Summit shows off a number of them). These look likely to ensure the platform remains relevant, even regaining ground in a world that has shifted, changing how and where Java is deployed. But that effort will only be valuable if new developers continue to learn Java in the first place - I think this JEP does go some way to addressing that. On Sat, Sep 16, 2023 at 5:40?AM Arne Styve wrote: > I recently came across this JEP, and was very surprised. Why on earth > should you add features to a professional programming language just to > apparently make it a bit easier for students and beginners to learn to > code? And especially when the suggested feature has no real use (and cannot > be used) once you learn to program OOP properly? > > > > I?ve been a SW-developer and manager for more than 20 years, and a teacher > of OOP at University for 15 years, and have never, ever experienced that > the public static void main(String[] args) have been remotely problematic > for students in learning to program. > > > > Please have this JEP terminated. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Sat Sep 16 13:03:53 2023 From: redio.development at gmail.com (Red IO) Date: Sat, 16 Sep 2023 15:03:53 +0200 Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move forward! In-Reply-To: References: Message-ID: I really disagree that this feature is a feature for beginners without value and "cannot be used once you learn oop properly". I don't see why you wouldn't be able to use the feature once you learn oop. As far as i understood one can have unnamed classes and other normal classes in the same project. Meaning you can have a file like Programm.java with the "void main() {}" in it in the root source folder and the packages, modules and classes besides it. It also makes scripts much easier to write in Java as one can just create a file MyScript.java with the main in it and launch it with "java MyScript.java". Both use cases have nothing to do with students. Fact is the entry point doesn't benefit from the class it's declared in. I would say this jep removes unnecessary restrictions from Java instead of adding unnecessary features. Java is the only language I know that has such a complicated entry point. Some languages let you start in the file directly or require to declare a main, start.. function. But no language but java requires all this class nonsense for the entry point. Great regards RedIODev On Fri, Sep 15, 2023, 19:40 Arne Styve wrote: > I recently came across this JEP, and was very surprised. Why on earth > should you add features to a professional programming language just to > apparently make it a bit easier for students and beginners to learn to > code? And especially when the suggested feature has no real use (and cannot > be used) once you learn to program OOP properly? > > > > I?ve been a SW-developer and manager for more than 20 years, and a teacher > of OOP at University for 15 years, and have never, ever experienced that > the public static void main(String[] args) have been remotely problematic > for students in learning to program. > > > > Please have this JEP terminated. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From numeralnathan at gmail.com Sat Sep 16 14:28:51 2023 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Sat, 16 Sep 2023 07:28:51 -0700 Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move forward! In-Reply-To: References: Message-ID: I haven't decided if I am for or against this JEP. I would like to add a few thoughts. They lean away from the JEP, but don't interpret that as my rejection of the JEP. I don't write main(). I use a framework and it has a main class to start the application server. Out of the many users of the framework, only the framework maintainer wrote a main(). So, any discussion about main() is really for the maintainer and those not using a framework that has a main class. I put all my server startup initialization in several javax.enterprise.context. at Initialized. I need a class around the @Initialized method to execute a bunch of logic. On the other hand, I wrote a Swing application and a few other utility programs. These have a main() method. I only wrote the main() method one time. I made a few changes over the years to the main(). main() has largely been forgotten as I spend most of my time in other parts of these programs. The main() method is the least of my concerns. Having a special way to write main() means a new syntax. It means learning more and not less. Sure, it might mean the learning curve is less steep (learn less upfront) but now the curve is higher (learn more overall). One has to learn the traditional way to write main(). However, the height of the curve is probably not much higher. Once someone has learned about classes, access restrictions (i.e., public), return types including the weird void, method parameters, static, then the traditional main() is just another method with special use. I can see how having a simple main() would allow people to learn about these other things gradually and build up to the traditional main(). On Sat, Sep 16, 2023 at 6:08?AM Red IO wrote: > I really disagree that this feature is a feature for beginners without > value and "cannot be used once you learn oop properly". > I don't see why you wouldn't be able to use the feature once you learn > oop. As far as i understood one can have unnamed classes and other normal > classes in the same project. Meaning you can have a file like Programm.java > with the "void main() {}" in it in the root source folder and the packages, > modules and classes besides it. > It also makes scripts much easier to write in Java as one can just create > a file MyScript.java with the main in it and launch it with "java > MyScript.java". > Both use cases have nothing to do with students. Fact is the entry point > doesn't benefit from the class it's declared in. I would say this jep > removes unnecessary restrictions from Java instead of adding unnecessary > features. Java is the only language I know that has such a complicated > entry point. Some languages let you start in the file directly or require > to declare a main, start.. function. But no language but java requires all > this class nonsense for the entry point. > > Great regards > RedIODev > > On Fri, Sep 15, 2023, 19:40 Arne Styve wrote: > >> I recently came across this JEP, and was very surprised. Why on earth >> should you add features to a professional programming language just to >> apparently make it a bit easier for students and beginners to learn to >> code? And especially when the suggested feature has no real use (and cannot >> be used) once you learn to program OOP properly? >> >> >> >> I?ve been a SW-developer and manager for more than 20 years, and a >> teacher of OOP at University for 15 years, and have never, ever experienced >> that the public static void main(String[] args) have been remotely >> problematic for students in learning to program. >> >> >> >> Please have this JEP terminated. >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Sat Sep 16 14:28:58 2023 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Sat, 16 Sep 2023 15:28:58 +0100 Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move forward! In-Reply-To: References: Message-ID: That does not need any changes in the language spec, compiler, etc. Moreover, there is already JShell - which solves the problem you are trying to carve, and I don't feel the tool is anyhow popular. It could give you an idea how "important" the JEP is... On Sat, 16 Sept 2023 at 14:08, Red IO wrote: > I really disagree that this feature is a feature for beginners without > value and "cannot be used once you learn oop properly". > I don't see why you wouldn't be able to use the feature once you learn > oop. As far as i understood one can have unnamed classes and other normal > classes in the same project. Meaning you can have a file like Programm.java > with the "void main() {}" in it in the root source folder and the packages, > modules and classes besides it. > It also makes scripts much easier to write in Java as one can just create > a file MyScript.java with the main in it and launch it with "java > MyScript.java". > Both use cases have nothing to do with students. Fact is the entry point > doesn't benefit from the class it's declared in. I would say this jep > removes unnecessary restrictions from Java instead of adding unnecessary > features. Java is the only language I know that has such a complicated > entry point. Some languages let you start in the file directly or require > to declare a main, start.. function. But no language but java requires all > this class nonsense for the entry point. > > Great regards > RedIODev > > On Fri, Sep 15, 2023, 19:40 Arne Styve wrote: > >> I recently came across this JEP, and was very surprised. Why on earth >> should you add features to a professional programming language just to >> apparently make it a bit easier for students and beginners to learn to >> code? And especially when the suggested feature has no real use (and cannot >> be used) once you learn to program OOP properly? >> >> >> >> I?ve been a SW-developer and manager for more than 20 years, and a >> teacher of OOP at University for 15 years, and have never, ever experienced >> that the public static void main(String[] args) have been remotely >> problematic for students in learning to program. >> >> >> >> Please have this JEP terminated. >> >> >> > -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Sep 17 20:07:11 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 17 Sep 2023 22:07:11 +0200 (CEST) Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move forward! In-Reply-To: References: Message-ID: <1412402519.41360010.1694981231328.JavaMail.zimbra@univ-eiffel.fr> > From: "Arne Styve" > To: "amber-dev" > Sent: Friday, September 15, 2023 5:08:04 PM > Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move > forward! > I recently came across this JEP, and was very surprised. Why on earth should you > add features to a professional programming language just to apparently make it > a bit easier for students and beginners to learn to code? And especially when > the suggested feature has no real use (and cannot be used) once you learn to > program OOP properly? > I?ve been a SW-developer and manager for more than 20 years, and a teacher of > OOP at University for 15 years, and have never, ever experienced that the > public static void main(String[] args) have been remotely problematic for > students in learning to program. > Please have this JEP terminated. This JEP is really about Java enboarding, i.e. how it is easy to use Java in the small. In Java, there are 3 level of encapsulation, you have the class, the package and the module. These levels helps developers to focus on what is important and avoid to have to specify what is not, depending on the kind of program you want to write. Currently, there is an unamed package, there is an unnamed module, but there is no unnamed class. This JEP aims to fix that. Having an unnamed class is not only important for teaching, it's also important for scripting. Scripting in Java is great and not so great at the same time. It's great because the script will work the same way on all major platforms even Windows and not so great because there is the unecessary ceremony of creating a class when what you really want is write a small script. I think that people should be albe to write their scripts in Java. That why we have added the support of shebang in JEP 330. That's why there is a JEP in draft to allow to split a script into several files [1]. And that's why there is this JEP, that simplify the creation of scripts. And as a teacher myself, there are several reasons why creating a class and writing "static void main(String[] args)" without understanding what it means is a bad idea. - We are showing how to create something before showing how to use it, it should be the other way around, - Understanding what 'static' means is not obvious, it has several meanings and it's hard to understand what it is without understanding what an instance is first, - Understanding what a class is is not obvious, it's easier to start by using the JDK classes before creating your own classes, - Some students believe a computer is magic, starting by an incantation is not the right way to demystify it. regards, R?mi [1] https://openjdk.org/jeps/8304400 -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Sep 18 07:36:50 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 18 Sep 2023 09:36:50 +0200 (CEST) Subject: Missing warning when using an instance main method Message-ID: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> Hello, I believe this code should emit a warning when compiled given it uses an instance main method class MissingWarnings { void main() { } } But the command javac --enable-preview --source 21 MissingWarnings.java does not print a warning. regards, R?mi From holo3146 at gmail.com Mon Sep 18 07:51:55 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Mon, 18 Sep 2023 09:51:55 +0200 Subject: Missing warning when using an instance main method In-Reply-To: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> References: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Instance main method was introduced in JEP 445, this instance main method is in principle identical to the ""top level"" main method introduced in the same JEP (it is not really top level, but an instance method of an unnamed class). Why should this issued a warning? On Mon, Sep 18, 2023, 09:37 Remi Forax wrote: > Hello, > I believe this code should emit a warning when compiled given it uses an > instance main method > > class MissingWarnings { > void main() { > } > } > > But the command > javac --enable-preview --source 21 MissingWarnings.java > does not print a warning. > > regards, > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Sep 18 08:46:28 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 18 Sep 2023 10:46:28 +0200 (CEST) Subject: Missing warning when using an instance main method In-Reply-To: References: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <715671078.41677921.1695026788296.JavaMail.zimbra@univ-eiffel.fr> > From: "Holo The Sage Wolf" > To: "Remi Forax" > Cc: "amber-dev" > Sent: Monday, September 18, 2023 9:51:55 AM > Subject: Re: Missing warning when using an instance main method > Instance main method was introduced in JEP 445, this instance main method is in > principle identical to the ""top level"" main method introduced in the same JEP > (it is not really top level, but an instance method of an unnamed class). > Why should this issued a warning? All preview features emit a warning at compile time. Using an unnamed class emits a warning But using an instance main method does not. regards, R?mi > On Mon, Sep 18, 2023, 09:37 Remi Forax < [ mailto:forax at univ-mlv.fr | > forax at univ-mlv.fr ] > wrote: >> Hello, >> I believe this code should emit a warning when compiled given it uses an >> instance main method >> class MissingWarnings { >> void main() { >> } >> } >> But the command >> javac --enable-preview --source 21 MissingWarnings.java >> does not print a warning. >> regards, >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Mon Sep 18 09:28:25 2023 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 18 Sep 2023 09:28:25 +0000 Subject: Missing warning when using an instance main method In-Reply-To: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> References: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> Message-ID: The issue is that the preview runtime check relies on the use of preview classes. There are no preview classes involved here. File a bug for JDK 22. We can special case this situation. Cheers, ? Jim ? > On Sep 18, 2023, at 4:37 AM, Remi Forax wrote: > > ?Hello, > I believe this code should emit a warning when compiled given it uses an instance main method > > class MissingWarnings { > void main() { > } > } > > But the command > javac --enable-preview --source 21 MissingWarnings.java > does not print a warning. > > regards, > R?mi From pedro.lamarao at prodist.com.br Mon Sep 18 12:54:47 2023 From: pedro.lamarao at prodist.com.br (=?UTF-8?Q?Pedro_Lamar=C3=A3o?=) Date: Mon, 18 Sep 2023 09:54:47 -0300 Subject: JEP 445: Unnamed Classes and Instance Main Methods - don not move forward! In-Reply-To: <1412402519.41360010.1694981231328.JavaMail.zimbra@univ-eiffel.fr> References: <1412402519.41360010.1694981231328.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Em dom., 17 de set. de 2023 ?s 17:08, Remi Forax escreveu: > *From: *"Arne Styve" > *To: *"amber-dev" > *Sent: *Friday, September 15, 2023 5:08:04 PM > *Subject: *JEP 445: Unnamed Classes and Instance Main Methods - don not > move forward! > > > This JEP is really about Java enboarding, i.e. how it is easy to use Java > in the small. > (...) > Having an unnamed class is not only important for teaching, it's also > important for scripting. Scripting in Java is great and not so great at the > same time. It's great because the script will work the same way on all > major platforms even Windows and not so great because there is the > unecessary ceremony of creating a class when what you really want is write > a small script. > I think that people should be albe to write their scripts in Java. That > why we have added the support of shebang in JEP 330. That's why there is a > JEP in draft to allow to split a script into several files [1]. And that's > why there is this JEP, that simplify the creation of scripts. > This is the kind of benefit I envision for my team. We frequently do small tools, Picocli based, where a single class is enough. In the definition of these tools, packages and classes, public vs. private, static vs. instance, are distractions to my junior team of maintainers. I expect to move the practice of small tools to the new syntax as soon as we upgrade the runtime in each case. -- Pedro Lamar?o -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Sep 18 14:17:59 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 18 Sep 2023 16:17:59 +0200 Subject: Missing warning when using an instance main method In-Reply-To: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> References: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> Message-ID: But this code is a perfectly valid code in non-preview Java version (unless you try to launch this class, but you don't know this during the compilation). I don't think there should be a warning. With best regards, Tagir Valeev. On Mon, Sep 18, 2023 at 9:37?AM Remi Forax wrote: > > Hello, > I believe this code should emit a warning when compiled given it uses an instance main method > > class MissingWarnings { > void main() { > } > } > > But the command > javac --enable-preview --source 21 MissingWarnings.java > does not print a warning. > > regards, > R?mi From alex.buckley at oracle.com Mon Sep 18 16:47:16 2023 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 18 Sep 2023 09:47:16 -0700 Subject: Missing warning when using an instance main method In-Reply-To: References: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <7a7d2695-3474-4ff3-8b88-80300553bea9@oracle.com> Traditionally, `void main()` was not an entrypoint, but under JEP 445, it is. This is a source compatible change, but not a behaviorally compatible change. For example, consider a slightly modified example: class MissingWarningsSuper { public static void main(String[] args) {} } class MissingWarnings extends MissingWarningsSuper { void main() {} } Traditionally, `java MissingWarnings` would have run the inherited static main. Under JEP 445, `java MissingWarnings` will run the declared instance main. (JLS 12.1.4 has a nice discussion of this policy.) I think a preview warning is appropriate in order to highlight the new role enjoyed by various methods called "main". Alex On 9/18/2023 7:17 AM, Tagir Valeev wrote: > But this code is a perfectly valid code in non-preview Java version > (unless you try to launch this class, but you don't know this during > the compilation). I don't think there should be a warning. > > With best regards, > Tagir Valeev. > > On Mon, Sep 18, 2023 at 9:37?AM Remi Forax wrote: >> >> Hello, >> I believe this code should emit a warning when compiled given it uses an instance main method >> >> class MissingWarnings { >> void main() { >> } >> } >> >> But the command >> javac --enable-preview --source 21 MissingWarnings.java >> does not print a warning. >> >> regards, >> R?mi From brian.goetz at oracle.com Mon Sep 18 17:17:36 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 18 Sep 2023 13:17:36 -0400 Subject: Missing warning when using an instance main method In-Reply-To: <7a7d2695-3474-4ff3-8b88-80300553bea9@oracle.com> References: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> <7a7d2695-3474-4ff3-8b88-80300553bea9@oracle.com> Message-ID: <260c2cde-bcef-550b-ce74-31a9fbb477dc@oracle.com> I think I'm closer to Tagir on this one.? We have never tried to warn the user when they mangle the "main" entry point before; we didn't warn on ??? void main(String[] args)? // no static or ??? static void main()??????? // no args or ??? static void main(Sling[] args)? // wrong args type These were all valid Java method declarations, but might not do what the author expected, because the launch protocol has always been nearly extralinguistic.? This JEP doesn't really bring the launch protocol that much more into the spotlight. I do think it is fair game for an IDE inspection, though (as if Tagir doesn't already have one.) On 9/18/2023 12:47 PM, Alex Buckley wrote: > Traditionally, `void main()` was not an entrypoint, but under JEP 445, > it is. This is a source compatible change, but not a behaviorally > compatible change. For example, consider a slightly modified example: > > class MissingWarningsSuper { > ??? public static void main(String[] args) {} > } > > class MissingWarnings extends MissingWarningsSuper { > ??? void main() {} > } > > Traditionally, `java MissingWarnings` would have run the inherited > static main. Under JEP 445, `java MissingWarnings` will run the > declared instance main. (JLS 12.1.4 has a nice discussion of this > policy.) > > I think a preview warning is appropriate in order to highlight the new > role enjoyed by various methods called "main". > > Alex > > On 9/18/2023 7:17 AM, Tagir Valeev wrote: >> But this code is a perfectly valid code in non-preview Java version >> (unless you try to launch this class, but you don't know this during >> the compilation). I don't think there should be a warning. >> >> With best regards, >> Tagir Valeev. >> >> On Mon, Sep 18, 2023 at 9:37?AM Remi Forax wrote: >>> >>> Hello, >>> I believe this code should emit a warning when compiled given it >>> uses an instance main method >>> >>> class MissingWarnings { >>> ?? void main() { >>> ?? } >>> } >>> >>> But the command >>> ?? javac --enable-preview --source 21 MissingWarnings.java >>> does not print a warning. >>> >>> regards, >>> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From alex.buckley at oracle.com Mon Sep 18 17:29:58 2023 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 18 Sep 2023 10:29:58 -0700 Subject: Missing warning when using an instance main method In-Reply-To: <260c2cde-bcef-550b-ce74-31a9fbb477dc@oracle.com> References: <1141965017.41603623.1695022610144.JavaMail.zimbra@univ-eiffel.fr> <7a7d2695-3474-4ff3-8b88-80300553bea9@oracle.com> <260c2cde-bcef-550b-ce74-31a9fbb477dc@oracle.com> Message-ID: <8e3e7ccf-f16a-452a-8921-af8a69479299@oracle.com> I'm not suggesting warnings around `main` signatures in the long term. Just that, if someone says --enable-preview, they receive warnings about any `main` signatures specified to be "candidate methods" in JLS 12.1 [1]. In other words, I think declaring `void main() {}` === using a preview language feature (assuming "Unnamed Classes and Instance main Methods" will have a second preview in 22). Alex [1] https://cr.openjdk.org/~gbierman/jep445/jep445-20230524/specs/unnamed-classes-instance-main-methods-jls.html#jls-12.1.4 On 9/18/2023 10:17 AM, Brian Goetz wrote: > I think I'm closer to Tagir on this one.? We have never tried to warn > the user when they mangle the "main" entry point before; we didn't warn on > > ??? void main(String[] args)? // no static > > or > > ??? static void main()??????? // no args > > or > > ??? static void main(Sling[] args)? // wrong args type > > > These were all valid Java method declarations, but might not do what the > author expected, because the launch protocol has always been nearly > extralinguistic.? This JEP doesn't really bring the launch protocol that > much more into the spotlight. > > I do think it is fair game for an IDE inspection, though (as if Tagir > doesn't already have one.) > > > On 9/18/2023 12:47 PM, Alex Buckley wrote: >> Traditionally, `void main()` was not an entrypoint, but under JEP 445, >> it is. This is a source compatible change, but not a behaviorally >> compatible change. For example, consider a slightly modified example: >> >> class MissingWarningsSuper { >> ??? public static void main(String[] args) {} >> } >> >> class MissingWarnings extends MissingWarningsSuper { >> ??? void main() {} >> } >> >> Traditionally, `java MissingWarnings` would have run the inherited >> static main. Under JEP 445, `java MissingWarnings` will run the >> declared instance main. (JLS 12.1.4 has a nice discussion of this >> policy.) >> >> I think a preview warning is appropriate in order to highlight the new >> role enjoyed by various methods called "main". >> >> Alex >> >> On 9/18/2023 7:17 AM, Tagir Valeev wrote: >>> But this code is a perfectly valid code in non-preview Java version >>> (unless you try to launch this class, but you don't know this during >>> the compilation). I don't think there should be a warning. >>> >>> With best regards, >>> Tagir Valeev. >>> >>> On Mon, Sep 18, 2023 at 9:37?AM Remi Forax wrote: >>>> >>>> Hello, >>>> I believe this code should emit a warning when compiled given it >>>> uses an instance main method >>>> >>>> class MissingWarnings { >>>> ?? void main() { >>>> ?? } >>>> } >>>> >>>> But the command >>>> ?? javac --enable-preview --source 21 MissingWarnings.java >>>> does not print a warning. >>>> >>>> regards, >>>> R?mi > From davidalayachew at gmail.com Thu Sep 21 17:00:17 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 21 Sep 2023 13:00:17 -0400 Subject: Question about records and overriding their "magic methods" Message-ID: Hello Amber Dev Team, Let's say I have a record like the following. record ComplexRecord(int blah /* and many more components */) {} Next, let's say that I want to override my toString, to include some derived information. Obviously, putting the derived info into the toString is easy, but how do I go about INCLUDING it with my original implementation of toString that was magically created for me by Java? If I try to fully recreate my toString, I run the risk of it becoming out-of-date upon refactor. Best I can come up with is nesting another record with the toString overloaded. Also not ideal. Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Sep 21 17:18:13 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 21 Sep 2023 13:18:13 -0400 Subject: Question about records and overriding their "magic methods" In-Reply-To: References: Message-ID: <516cb2d2-5aac-cbe1-5f01-8a0a64358119@oracle.com> We explored this topic somewhat when designing the feature. When you override a method x, you can still delegate (via super.x()) to the thing you override.? We considered doing the same here (delegating to default.x()), but concluded that this failed the power-to-weight-ratio test, because usually you do not want to _refine_ an equals/toString/hashCode calculation, but instead broaden it (such as comparing arrays by contents rather than by ==.) If you pull on this "string" a bit, the API that you would want here is complex and enormous, and has as many tuning knobs as a Lombok.? So refactoring records that customize Object methods becomes a higher-responsibility activity.? Leave "future you" some comments for what you were thinking today. On 9/21/2023 1:00 PM, David Alayachew wrote: > Hello Amber Dev Team, > > Let's say I have a record like the following. > > record ComplexRecord(int blah /* and many more components */) {} > > Next, let's say that I want to override my toString, to include some > derived information. > > Obviously, putting the derived info into the toString is easy, but how > do I go about INCLUDING it with my original implementation of toString > that was magically created for me by Java? > > If I try to fully recreate my toString, I run the risk of it becoming > out-of-date upon refactor. Best I can come up with is nesting another > record with the toString overloaded. Also not ideal. > > Thank you for your time! > David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Sep 21 17:49:14 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 21 Sep 2023 19:49:14 +0200 (CEST) Subject: Question about records and overriding their "magic methods" In-Reply-To: <516cb2d2-5aac-cbe1-5f01-8a0a64358119@oracle.com> References: <516cb2d2-5aac-cbe1-5f01-8a0a64358119@oracle.com> Message-ID: <1530476177.250372.1695318554905.JavaMail.zimbra@univ-eiffel.fr> > From: "Brian Goetz" > To: "David Alayachew" , "amber-dev" > > Sent: Thursday, September 21, 2023 7:18:13 PM > Subject: Re: Question about records and overriding their "magic methods" > We explored this topic somewhat when designing the feature. > When you override a method x, you can still delegate (via super.x()) to the > thing you override. We considered doing the same here (delegating to > default.x()), but concluded that this failed the power-to-weight-ratio test, > because usually you do not want to _refine_ an equals/toString/hashCode > calculation, but instead broaden it (such as comparing arrays by contents > rather than by ==.) > If you pull on this "string" a bit, the API that you would want here is complex > and enormous, and has as many tuning knobs as a Lombok. So refactoring records > that customize Object methods becomes a higher-responsibility activity. Leave > "future you" some comments for what you were thinking today. If you really really want to do it, you can use the bootstrap method in java.lang.runtime.ObjectsMethods but it's ugly. public class CallingRecordToStringDemo { record Foo ( String blah, int value) { private static final MethodHandle TO_STRING ; static { var lookup = MethodHandles . lookup (); var components = Foo . class .getRecordComponents(); CallSite callsite ; try { callsite = ( CallSite ) ObjectMethods . bootstrap ( lookup , "toString" , MethodType . methodType ( String . class , Foo . class ), Foo . class , Arrays . stream ( components ).map( RecordComponent ::getName).collect( Collectors . joining ( ";" )), Arrays . stream ( components ).map(c -> { try { return lookup .unreflect(c.getAccessor()); } catch ( IllegalAccessException e) { throw new AssertionError(e); } }).toArray( MethodHandle []:: new )); } catch ( Throwable e) { throw new AssertionError(e); } TO_STRING = callsite .dynamicInvoker(); } @Override public String toString () { try { return ( String ) TO_STRING .invokeExact( this ); } catch ( Error e) { throw e; } catch ( Throwable e) { throw new AssertionError(e); } } } public static void main ( String [] args) { System . out .println( new Foo( "blah" , 42 )); } } We also do not want to close the door to supporting super.x() if at some point in the future if the VM spec is changed to introduce a way to generate bridge methods at runtime. The record methods toString/equals/hashCode will be declare as abstract bridge method in that case. regards, R?mi > On 9/21/2023 1:00 PM, David Alayachew wrote: >> Hello Amber Dev Team, >> Let's say I have a record like the following. >> record ComplexRecord(int blah /* and many more components */) {} >> Next, let's say that I want to override my toString, to include some derived >> information. >> Obviously, putting the derived info into the toString is easy, but how do I go >> about INCLUDING it with my original implementation of toString that was >> magically created for me by Java? >> If I try to fully recreate my toString, I run the risk of it becoming >> out-of-date upon refactor. Best I can come up with is nesting another record >> with the toString overloaded. Also not ideal. >> Thank you for your time! >> David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Sep 21 18:32:43 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 21 Sep 2023 14:32:43 -0400 Subject: Question about records and overriding their "magic methods" In-Reply-To: <516cb2d2-5aac-cbe1-5f01-8a0a64358119@oracle.com> References: <516cb2d2-5aac-cbe1-5f01-8a0a64358119@oracle.com> Message-ID: Hello Brian, Thank you for your response! > We explored this topic > ... > but concluded that this failed the power-to-weight-ratio > test Makes a lot of sense. This is only ever going to apply to "magic methods". > because usually you do not want to _refine_ an equals/toString/hashCode calculation, but instead broaden it Makes a lot of sense. In my ~1.5 years of using records, this is the second time I have ever had the desire to refine the outcome of a "magic method". > If you pull on this "string" a bit, the API that you > would want here is complex and enormous, and has as many > tuning knobs as a Lombok. I see what you mean. Today, I wanted to include a derived value to the toString. What if I also wanted to include these derived values "inside" of the square brackets? Makes a lot of sense. > Leave "future you" some comments for what you were > thinking today. Makes sense. I think the value of overriding toString is not very much in the long run, so rather than overriding, I'll just take the extra lap and insert the .map() call to my streams instead of being lazy. Or I could do the "ugly" solution that R?mi came up with lol. Thank you for your time and help! David Alayachew On Thu, Sep 21, 2023 at 1:18?PM Brian Goetz wrote: > We explored this topic somewhat when designing the feature. > > When you override a method x, you can still delegate (via super.x()) to > the thing you override. We considered doing the same here (delegating to > default.x()), but concluded that this failed the power-to-weight-ratio > test, because usually you do not want to _refine_ an > equals/toString/hashCode calculation, but instead broaden it (such as > comparing arrays by contents rather than by ==.) > > If you pull on this "string" a bit, the API that you would want here is > complex and enormous, and has as many tuning knobs as a Lombok. So > refactoring records that customize Object methods becomes a > higher-responsibility activity. Leave "future you" some comments for what > you were thinking today. > > > > On 9/21/2023 1:00 PM, David Alayachew wrote: > > Hello Amber Dev Team, > > Let's say I have a record like the following. > > record ComplexRecord(int blah /* and many more components */) {} > > Next, let's say that I want to override my toString, to include some > derived information. > > Obviously, putting the derived info into the toString is easy, but how do > I go about INCLUDING it with my original implementation of toString that > was magically created for me by Java? > > If I try to fully recreate my toString, I run the risk of it becoming > out-of-date upon refactor. Best I can come up with is nesting another > record with the toString overloaded. Also not ideal. > > Thank you for your time! > David Alayachew > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Sep 21 18:40:05 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 21 Sep 2023 14:40:05 -0400 Subject: Question about records and overriding their "magic methods" In-Reply-To: <1530476177.250372.1695318554905.JavaMail.zimbra@univ-eiffel.fr> References: <516cb2d2-5aac-cbe1-5f01-8a0a64358119@oracle.com> <1530476177.250372.1695318554905.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hello R?mi, Thank you for your response! > If you really really want to do it, you can use the > bootstrap method in java.lang.runtime.ObjectsMethods but > it's ugly. That's actually way better than I thought it would be. It's surprisingly direct too. > We also do not want to close the door to supporting > super.x() if at some point in the future if the VM spec > is changed to introduce a way to generate bridge methods > at runtime. > > The record methods toString/equals/hashCode will be > declare as abstract bridge method in that case. I brushed up on bridge methods for this response - https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html I get the concept, but I am curious what having that at runtime would give us that we don't already get at compile time. I don't quite see it. Thank you for your time and help! David Alayachew On Thu, Sep 21, 2023 at 1:49?PM Remi Forax wrote: > > > ------------------------------ > > *From: *"Brian Goetz" > *To: *"David Alayachew" , "amber-dev" < > amber-dev at openjdk.org> > *Sent: *Thursday, September 21, 2023 7:18:13 PM > *Subject: *Re: Question about records and overriding their "magic methods" > > We explored this topic somewhat when designing the feature. > > When you override a method x, you can still delegate (via super.x()) to > the thing you override. We considered doing the same here (delegating to > default.x()), but concluded that this failed the power-to-weight-ratio > test, because usually you do not want to _refine_ an > equals/toString/hashCode calculation, but instead broaden it (such as > comparing arrays by contents rather than by ==.) > > If you pull on this "string" a bit, the API that you would want here is > complex and enormous, and has as many tuning knobs as a Lombok. So > refactoring records that customize Object methods becomes a > higher-responsibility activity. Leave "future you" some comments for what > you were thinking today. > > > > If you really really want to do it, you can use the bootstrap method in > java.lang.runtime.ObjectsMethods but it's ugly. > > public class CallingRecordToStringDemo { > > record Foo(String blah, int value) { > private static final MethodHandle TO_STRING; > static { > var lookup = MethodHandles.lookup(); > var components = Foo.class.getRecordComponents(); > CallSite callsite; > try { > callsite = (CallSite) ObjectMethods.bootstrap(lookup, > "toString", > MethodType.methodType(String.class, Foo.class), > Foo.class, > Arrays.stream(components).map(RecordComponent::getName).collect(Collectors.joining(";")), > Arrays.stream(components).map(c -> { > try { > return lookup.unreflect(c.getAccessor()); > } catch (IllegalAccessException e) { > throw new AssertionError(e); > } > }).toArray(MethodHandle[]::new)); > } catch (Throwable e) { > throw new AssertionError(e); > } > TO_STRING = callsite.dynamicInvoker(); > } > > @Override > public String toString() { > try { > return (String) TO_STRING.invokeExact(this); > } catch (Error e) { > throw e; > } catch (Throwable e) { > throw new AssertionError(e); > } > } > } > > public static void main(String[] args) { > System.out.println(new Foo("blah", 42)); > } > } > > > We also do not want to close the door to supporting super.x() if at some > point in the future if the VM spec is changed to introduce a way to > generate bridge methods at runtime. > The record methods toString/equals/hashCode will be declare as abstract > bridge method in that case. > > regards, > R?mi > > > > > On 9/21/2023 1:00 PM, David Alayachew wrote: > > Hello Amber Dev Team, > > Let's say I have a record like the following. > > record ComplexRecord(int blah /* and many more components */) {} > > Next, let's say that I want to override my toString, to include some > derived information. > > Obviously, putting the derived info into the toString is easy, but how do > I go about INCLUDING it with my original implementation of toString that > was magically created for me by Java? > > If I try to fully recreate my toString, I run the risk of it becoming > out-of-date upon refactor. Best I can come up with is nesting another > record with the toString overloaded. Also not ideal. > > Thank you for your time! > David Alayachew > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Sep 23 06:53:18 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sat, 23 Sep 2023 08:53:18 +0200 (CEST) Subject: Question about records and overriding their "magic methods" In-Reply-To: References: <516cb2d2-5aac-cbe1-5f01-8a0a64358119@oracle.com> <1530476177.250372.1695318554905.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <1631115398.1669354.1695451998528.JavaMail.zimbra@univ-eiffel.fr> > From: "David Alayachew" > To: "Remi Forax" > Cc: "Brian Goetz" , "amber-dev" > Sent: Thursday, September 21, 2023 8:40:05 PM > Subject: Re: Question about records and overriding their "magic methods" > Hello R?mi, > Thank you for your response! > > If you really really want to do it, you can use the > > bootstrap method in java.lang.runtime.ObjectsMethods but > > it's ugly. > That's actually way better than I thought it would be. It's surprisingly direct > too. > > We also do not want to close the door to supporting > > super.x() if at some point in the future if the VM spec > > is changed to introduce a way to generate bridge methods > > at runtime. > > The record methods toString/equals/hashCode will be > > declare as abstract bridge method in that case. > I brushed up on bridge methods for this response - [ > https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html | > https://docs.oracle.com/javase/tutorial/java/generics/bridgeMethods.html ] > I get the concept, but I am curious what having that at runtime would give us > that we don't already get at compile time. I don't quite see it. The idea is to use a method handle to implement those dynamically. Anyway, thinking a little bit more, there is perhaps a simpler solution. Let state the issue, toString/equals/hashCode are abstract in Record, so you can not call super.toString(). If we want to support that, it means that either as Brian said we invent a way to ask for a default implementation provided by the compiler with a new syntax or we make toString/equals/hashCode non-abstract and we provide a code for them. As Brian said, the former is too adhoc and the latter run into the fact that while we can provide generic implementations inside java.lang.Record, those implementations will be slow because inside java.lang.Record we need to dynamically ask what is the actual record and then call a generic code with that record class. Another possible solution is to implement Record.equals/toString/hashCode to throw an exception saying they are magic methods and to re-route all access to use an invokedynamic instead. So - the compiler should trap the calls to super.toString()/equals()/hashCode() and generate an invokedynamic instead. - the VM should trap reflection calls and method handle creations to use a method handle produced by ObjectMethods.bootstrap() instead. For me, that's a lot of engineering for a use case that will be used once in a while. > Thank you for your time and help! > David Alayachew Regards, R?mi PS: we may be able in the future when specialized generics will be landed to re-visit that issue because we will get a way to have static method code specialized by a constant record class but this will still require a lot of engineering, i am afraid. > On Thu, Sep 21, 2023 at 1:49 PM Remi Forax < [ mailto:forax at univ-mlv.fr | > forax at univ-mlv.fr ] > wrote: >>> From: "Brian Goetz" < [ mailto:brian.goetz at oracle.com | brian.goetz at oracle.com ] >>> > >>> To: "David Alayachew" < [ mailto:davidalayachew at gmail.com | >>> davidalayachew at gmail.com ] >, "amber-dev" < [ mailto:amber-dev at openjdk.org | >>> amber-dev at openjdk.org ] > >>> Sent: Thursday, September 21, 2023 7:18:13 PM >>> Subject: Re: Question about records and overriding their "magic methods" >>> We explored this topic somewhat when designing the feature. >>> When you override a method x, you can still delegate (via super.x()) to the >>> thing you override. We considered doing the same here (delegating to >>> default.x()), but concluded that this failed the power-to-weight-ratio test, >>> because usually you do not want to _refine_ an equals/toString/hashCode >>> calculation, but instead broaden it (such as comparing arrays by contents >>> rather than by ==.) >>> If you pull on this "string" a bit, the API that you would want here is complex >>> and enormous, and has as many tuning knobs as a Lombok. So refactoring records >>> that customize Object methods becomes a higher-responsibility activity. Leave >>> "future you" some comments for what you were thinking today. >> If you really really want to do it, you can use the bootstrap method in >> java.lang.runtime.ObjectsMethods but it's ugly. >> public class CallingRecordToStringDemo { >> record Foo ( String blah, int value) { >> private static final MethodHandle TO_STRING ; >> static { >> var lookup = MethodHandles . lookup (); >> var components = Foo . class .getRecordComponents(); >> CallSite callsite ; >> try { >> callsite = ( CallSite ) ObjectMethods . bootstrap ( lookup , >> "toString" , >> MethodType . methodType ( String . class , Foo . class ), >> Foo . class , >> Arrays . stream ( components ).map( RecordComponent ::getName).collect( >> Collectors . joining ( ";" )), >> Arrays . stream ( components ).map(c -> { >> try { >> return lookup .unreflect(c.getAccessor()); >> } catch ( IllegalAccessException e) { >> throw new AssertionError(e); >> } >> }).toArray( MethodHandle []:: new )); >> } catch ( Throwable e) { >> throw new AssertionError(e); >> } >> TO_STRING = callsite .dynamicInvoker(); >> } >> @Override >> public String toString () { >> try { >> return ( String ) TO_STRING .invokeExact( this ); >> } catch ( Error e) { >> throw e; >> } catch ( Throwable e) { >> throw new AssertionError(e); >> } >> } >> } >> public static void main ( String [] args) { >> System . out .println( new Foo( "blah" , 42 )); >> } >> } >> We also do not want to close the door to supporting super.x() if at some point >> in the future if the VM spec is changed to introduce a way to generate bridge >> methods at runtime. >> The record methods toString/equals/hashCode will be declare as abstract bridge >> method in that case. >> regards, >> R?mi >>> On 9/21/2023 1:00 PM, David Alayachew wrote: >>>> Hello Amber Dev Team, >>>> Let's say I have a record like the following. >>>> record ComplexRecord(int blah /* and many more components */) {} >>>> Next, let's say that I want to override my toString, to include some derived >>>> information. >>>> Obviously, putting the derived info into the toString is easy, but how do I go >>>> about INCLUDING it with my original implementation of toString that was >>>> magically created for me by Java? >>>> If I try to fully recreate my toString, I run the risk of it becoming >>>> out-of-date upon refactor. Best I can come up with is nesting another record >>>> with the toString overloaded. Also not ideal. >>>> Thank you for your time! >>>> David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From tzengshinfu at gmail.com Mon Sep 25 06:10:01 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Mon, 25 Sep 2023 14:10:01 +0800 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: Hi, Folks, Since my last suggestion, I've conducted some research and stumbled upon the `@Cleanup`[https://projectlombok.org/features/Cleanup] feature in the popular library `Lombok`. Personally, I believe that the existence of this feature indicates a certain demand, but it's unfortunate that it requires importing the library, which adds to the maintenance complexity. I'd like to hear your thoughts on this situation. Is importing Lombok the best approach? P.S. This is my second email to the mail list. If there's anything I may have overlooked or done incorrectly, please feel free to let me know. Thank you. Thanks for your interest and support, Hsinfu Tseng /* GET BETTER EVERY DAY */ tzengshinfu ? 2023?9?4? ?? ??11:14??? > > Hi, Folks: > > Recently, I've been using `JBang` to write some script tools, and I > feel that Java has transformed with modern syntax features like > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly > added language features provide developers with more options in > specific scenarios, almost like giving Java a set of wings. > > When using the `try-with-resources statement`, I thought it might be > possible to introduce an additional simplified syntax, which I believe > would be useful when developing simple, small-scale programs. > Meanwhile, scenarios requiring resource closure timing specification > or exception handling can still make use of the original syntax. > > At first glance, this simplified syntax may seem a bit unusual, but > considering that `switch` itself encompasses both statements and > expressions, and that `switch > expression`[https://openjdk.org/jeps/361] also adds a semicolon at the > end of `}`, perhaps it won't be as difficult to accept. > > Because it reduces indentation, making the logic clearer and more > visible, users don't need to consider resource closure timing; they > can focus solely on business logic. It's possible that in the future, > the likelihood of using this simplified syntax will increase. > > In my personal opinion, the original `try-with-resources statement` > feels like driving a manual transmission car, where I have full > control over everything. However, if we can let the runtime manage it > autonomously, it's like driving an automatic car on the long journey > of coding, and even more effortless. > > If there could be an alternative approach that is both hassle-free and > doesn't require altering Java specifications, I hope that experienced > individuals can share their insights. > > > Thanks for your interest and support, > Hsinfu Tseng > > (Using the JEP template because I've found it helps clarify my thoughts.) > > > ## Summary > > Allow omitting the definition of scope when declaring > `try-with-resources statement`. > > > ## Goals > > * Not introducing new scopes to reduce the cognitive burden on > developers, especially beginners. > * Reducing the interference of hierarchical indentation to enhance > code readability. > * The declaration position of variables is not restricted by > `try-with-resources statement`. > * Ensure that resources are released before the current scope ends. > > > ## Motivation > > The `try-with-resources statement` simplified resource management in > Java after version 7. However, its side effect is the necessity to > declare a scope and, at the same time, it restricts lifetime of the > variable declared within the scope: > > ```java > public class example1 { > public static void main(String... args) throws > FileNotFoundException, IOException { > try (BufferedReader reader = new BufferedReader(new > FileReader("studentName.txt"))) { > String studentName = "not found"; > /* Variable 'studentName' is only alive in the scope of > try-with-resources statement. */ > > while ((studentName = reader.readLine()) != null) { > break; > } > } > > System.out.println(studentName); > /* Variable 'studentName' cannot be resolved here. */ > } > } > > ``` > > While it's possible to move variable declaration before > `try-with-resources statement` and modify variable content within the > scope of `try-with-resources statement`, the actions of declaration, > assignment, and usage of variables are segmented by the scope of > `try-with-resources statement`. In practice, we would prefer variable > declarations to be as close as possible to their usage locations and > avoid unnecessary > separation[https://rules.sonarsource.com/java/RSPEC-1941/]: > > ```java > public class example2 { > public static void main(String... args) throws > FileNotFoundException, IOException { > String studentName = "not found"; > > // #region > // The scope of try-with-resources statement separates > // the declaration, assignment, and usage of variable 'studentName'. > try (BufferedReader reader = new BufferedReader(new > FileReader("studentName.txt"))) { > while ((studentName = reader.readLine()) != null) { > break; > } > } > // #endregion > > System.out.println(studentName); > } > } > > ``` > > Furthermore, methods that involve multiple resources requiring > management and dependencies might become cumbersome in terms of syntax > due to `nested try-with-resources statements`: > > ```java > public class example3 { > public static void main(String... args) throws SQLException { > String jdbcUrl = "jdbcUrl"; > String username = "username"; > String password = "password"; > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > username, password)) { > String selectSQL = "selectSQL"; > String studentID = "studentID"; > > try (PreparedStatement statement = > conn.prepareStatement(selectSQL)) { > statement.setString(1, studentID); > > try (ResultSet result = statement.executeQuery()) { > String studentName = "not found"; > > while (result.next()) { > studentName = result.getString(1); > } > > System.out.println(studentName); > } > } > } > } > } > > ``` > > This proposal introduces a shorthand declaration that eliminates the > scope limitations of `try-with-resources statement`. It enables > variable declaration, assignment, and usage within the same scope, > reduces indentation levels for improved code readability (particularly > in `nested try-with-resources statements`), and defers the timing of > resource release to the system, ensuring resource disposal before the > scope in `try-with-resources statement` is declared ends. (Similar to > Golang's `Defer statement` + resource > close[https://go.dev/tour/flowcontrol/12], C#'s `using > declarations`[https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using], > and Swift's `Defer statement` + resource > close[https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement]) > > > ## Description > > The format of the `simplified try-with-resources statement` is as follows: > > ```java > try (resources);/* Change '{}' to ';'. */ > > ``` > > It can be observed that omitting `{}` reduces the indentation, thus > enhancing code readability by not introducing new scopes. Moreover, > variable declarations can be positioned either before or after the > `simplified try-with-resources statement`: > > ```java > public class example1_simplified { > public static void main(String... args) throws > FileNotFoundException, IOException { > try (BufferedReader reader = new BufferedReader(new > FileReader("studentName.txt"))); > String studentName = "not found"; > > while ((studentName = reader.readLine()) != null) { > break; > } > > System.out.println(studentName); > } > } > > ``` > > Because it automatically converts the `simplified try-with-resources > statement` to an equivalent `try-with-resources statement` after > compilation: > > ```java > public class example1_converted { > public static void main(String... args) throws > FileNotFoundException, IOException { > try (BufferedReader reader = new BufferedReader(new > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */ > String studentName = "not found"; > > while ((studentName = reader.readLine()) != null) { > break; > } > > System.out.println(studentName); > }/* Add '}' before the current scope ends. */ > } > } > > ``` > > Because the scope of `simplified try-with-resources statement` extends > until the current scope is terminated, the variable `studentName`, > which originally couldn't be resolved in `example1`, can now be > resolved successfully. This allows variable declaration, assignment, > and usage within the same scope, ensuring that resource close > operations occur before the current scope ends. > > Invoking multiple dependent `simplified try-with-resources statements` > is similar to regular variable declarations, thereby avoiding > excessive nesting. > > ```java > public class example3_simplified { > public static void main(String... args) throws SQLException { > String jdbcUrl = "jdbcUrl"; > String username = "username"; > String password = "password"; > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > username, password)); > String selectSQL = "selectSQL"; > String studentID = "studentID"; > > try (PreparedStatement statement = conn.prepareStatement(selectSQL)); > statement.setString(1, studentID); > > try (ResultSet result = statement.executeQuery()); > String studentName = "not found"; > > while (result.next()) { > studentName = result.getString(1); > } > > System.out.println(studentName); > } > } > > ``` > > It will be converted to equivalent `nested try-with-resources > statements` after compilation: > > ```java > public class example3_converted { > public static void main(String... args) throws SQLException { > String jdbcUrl = "jdbcUrl"; > String username = "username"; > String password = "password"; > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > username, password)) {/* Change ';' back to '{'. */ > String selectSQL = "selectSQL"; > String studentID = "studentID"; > > try (PreparedStatement statement = > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */ > statement.setString(1, studentID); > > try (ResultSet result = statement.executeQuery()) {/* Change > ';' back to '{'. */ > String studentName = "not found"; > > while (result.next()) { > studentName = result.getString(1); > } > > System.out.println(studentName); > }}}}/* Add the corresponding number (in this case, 3) of '}' > before the current scope ends. */ > } > > ``` > > After code formatting: > > ```java > public class example3_formatted { > public static void main(String... args) throws SQLException { > String jdbcUrl = "jdbcUrl"; > String username = "username"; > String password = "password"; > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > username, password)) { > String selectSQL = "selectSQL"; > String studentID = "studentID"; > > try (PreparedStatement statement = > conn.prepareStatement(selectSQL)) { > statement.setString(1, studentID); > > try (ResultSet result = statement.executeQuery()) { > String studentName = "not found"; > > while (result.next()) { > studentName = result.getString(1); > } > > System.out.println(studentName); > } > } > } > } > } > > ``` > > Comparing the code structures of `example3_simplified` and > `example3_formatted`, it is evident that the usage of `simplified > try-with-resources statement` enhances code readability by > significantly reducing nested indentation levels. Additionally, it can > be observed that in the converted `nested try-with-resources > statements`, resource close actions will be executed in reverse order > of declaration, just before the current scope ends. > > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods > (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to > create a simple, small-scale program: > > ```java > String jdbcUrl = "jdbcUrl"; > String username = "username"; > String password = "password"; > String selectSQL = "selectSQL"; > String studentID = "studentID"; > > void main() throws SQLException { > try (Connection conn = DriverManager.getConnection(jdbcUrl, > username, password)); > try (PreparedStatement statement = conn.prepareStatement(selectSQL)); > statement.setString(1, studentID); > > try (ResultSet result = statement.executeQuery()); > String studentName = "not found"; > > while (result.next()) { > studentName = result.getString(1); > } > > System.out.println(studentName); > } > > ``` > > > ## Alternatives > > * One approach to mitigate the impact on readability caused by deeply > `nested try-with-resources statements` is to consolidate multiple > resources within the same `try-with-resources statement` as much as > possible. However, formatting multiple resources with line breaks can > also disrupt visual clarity, and furthermore, it's not always feasible > to close many resources simultaneously: > > ```java > public class alternative1 { > public static void main(String... args) throws SQLException { > String jdbcUrl = "jdbcUrl"; > String username = "username"; > String password = "password"; > String selectSQL = "selectSQL"; > String studentID = "studentID"; > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > username, password); > PreparedStatement statement = > conn.prepareStatement(selectSQL)) { > /* In this case, there are only 2 resources, but if there are 3 or more, > it will become even less aesthetically pleasing. */ > > statement.setString(1, studentID); > > try (ResultSet result = statement.executeQuery()) { > /* ResultSet 'result' cannot be closed simultaneously with > PreparedStatement 'statement' > because PreparedStatement 'statement' needs parameter > configuration first. */ > String studentName = "not found"; > > while (result.next()) { > studentName = result.getString(1); > } > > return studentName; > } > } > } > } > > ``` > > * Alternatively, creating a separate method to confine the scope of > managed resources within the method can be done, but it necessitates > 'naming' the new > method[https://www.karlton.org/2017/12/naming-things-hard/]. This > approach simply shifts complexity to the interior of additional > method, which may not be conducive to creating simple, small-scale > programs: > > ```java > public class alternative2 { > public static void main(String... args) throws SQLException { > String selectSQL = "selectSQL"; > String studentID = "studentID"; > String studentName = getStudentName(selectSQL, studentID); > > System.out.println(studentName); > } > > public static String getStudentName(String sql, String studentID) > throws SQLException { > String jdbcUrl = "jdbcUrl"; > String username = "username"; > String password = "password"; > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > username, password)) { > try (PreparedStatement statement = conn.prepareStatement(sql)) { > statement.setString(1, studentID); > > try (ResultSet result = statement.executeQuery()) { > String studentName = "not found"; > > while (result.next()) { > studentName = result.getString(1); > } > > return studentName; > } > } > } > } > } > > ``` > > /* GET BETTER EVERY DAY */ From amaembo at gmail.com Mon Sep 25 07:22:30 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 25 Sep 2023 09:22:30 +0200 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: Hello! Looking at 'vanilla Java' sample, I assume that there _was_ demand before Java 7. Now, it's basically covered with try-with-resources in a more explicit and robust way. This cannot serve as good statistical data, but I monitor support requests in IntelliJ IDEA, regarding Lombok. Due to the hacky nature of Lombok, we have many problems with it, and people report that some inspections don't play well with certain Lombok annotations. So far, I don't remember a single report concerning Cleanup annotation, which means that either it's perfectly supported (I doubt), or nobody uses it. Also, I should note that we are in the wrong mailing list. This list discusses the implementation of new language features, not their design. You probably need amber-spec-comments. With best regards, Tagir Valeev. On Mon, Sep 25, 2023, 09:13 tzengshinfu wrote: > Hi, Folks, > > Since my last suggestion, > I've conducted some research and stumbled upon the > `@Cleanup`[https://projectlombok.org/features/Cleanup] feature in the > popular library `Lombok`. > > Personally, I believe that the existence of this feature indicates a > certain demand, but it's unfortunate that it requires importing the > library, which adds to the maintenance complexity. > I'd like to hear your thoughts on this situation. Is importing Lombok > the best approach? > > P.S. This is my second email to the mail list. If there's anything I > may have overlooked or done incorrectly, please feel free to let me > know. Thank you. > > Thanks for your interest and support, > Hsinfu Tseng > > /* GET BETTER EVERY DAY */ > > > tzengshinfu ? 2023?9?4? ?? ??11:14??? > > > > Hi, Folks: > > > > Recently, I've been using `JBang` to write some script tools, and I > > feel that Java has transformed with modern syntax features like > > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly > > added language features provide developers with more options in > > specific scenarios, almost like giving Java a set of wings. > > > > When using the `try-with-resources statement`, I thought it might be > > possible to introduce an additional simplified syntax, which I believe > > would be useful when developing simple, small-scale programs. > > Meanwhile, scenarios requiring resource closure timing specification > > or exception handling can still make use of the original syntax. > > > > At first glance, this simplified syntax may seem a bit unusual, but > > considering that `switch` itself encompasses both statements and > > expressions, and that `switch > > expression`[https://openjdk.org/jeps/361] also adds a semicolon at the > > end of `}`, perhaps it won't be as difficult to accept. > > > > Because it reduces indentation, making the logic clearer and more > > visible, users don't need to consider resource closure timing; they > > can focus solely on business logic. It's possible that in the future, > > the likelihood of using this simplified syntax will increase. > > > > In my personal opinion, the original `try-with-resources statement` > > feels like driving a manual transmission car, where I have full > > control over everything. However, if we can let the runtime manage it > > autonomously, it's like driving an automatic car on the long journey > > of coding, and even more effortless. > > > > If there could be an alternative approach that is both hassle-free and > > doesn't require altering Java specifications, I hope that experienced > > individuals can share their insights. > > > > > > Thanks for your interest and support, > > Hsinfu Tseng > > > > (Using the JEP template because I've found it helps clarify my thoughts.) > > > > > > ## Summary > > > > Allow omitting the definition of scope when declaring > > `try-with-resources statement`. > > > > > > ## Goals > > > > * Not introducing new scopes to reduce the cognitive burden on > > developers, especially beginners. > > * Reducing the interference of hierarchical indentation to enhance > > code readability. > > * The declaration position of variables is not restricted by > > `try-with-resources statement`. > > * Ensure that resources are released before the current scope ends. > > > > > > ## Motivation > > > > The `try-with-resources statement` simplified resource management in > > Java after version 7. However, its side effect is the necessity to > > declare a scope and, at the same time, it restricts lifetime of the > > variable declared within the scope: > > > > ```java > > public class example1 { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) { > > String studentName = "not found"; > > /* Variable 'studentName' is only alive in the scope of > > try-with-resources statement. */ > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > } > > > > System.out.println(studentName); > > /* Variable 'studentName' cannot be resolved here. */ > > } > > } > > > > ``` > > > > While it's possible to move variable declaration before > > `try-with-resources statement` and modify variable content within the > > scope of `try-with-resources statement`, the actions of declaration, > > assignment, and usage of variables are segmented by the scope of > > `try-with-resources statement`. In practice, we would prefer variable > > declarations to be as close as possible to their usage locations and > > avoid unnecessary > > separation[https://rules.sonarsource.com/java/RSPEC-1941/]: > > > > ```java > > public class example2 { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > String studentName = "not found"; > > > > // #region > > // The scope of try-with-resources statement separates > > // the declaration, assignment, and usage of variable > 'studentName'. > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) { > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > } > > // #endregion > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > Furthermore, methods that involve multiple resources requiring > > management and dependencies might become cumbersome in terms of syntax > > due to `nested try-with-resources statements`: > > > > ```java > > public class example3 { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > } > > } > > } > > > > ``` > > > > This proposal introduces a shorthand declaration that eliminates the > > scope limitations of `try-with-resources statement`. It enables > > variable declaration, assignment, and usage within the same scope, > > reduces indentation levels for improved code readability (particularly > > in `nested try-with-resources statements`), and defers the timing of > > resource release to the system, ensuring resource disposal before the > > scope in `try-with-resources statement` is declared ends. (Similar to > > Golang's `Defer statement` + resource > > close[https://go.dev/tour/flowcontrol/12], C#'s `using > > declarations`[ > https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using > ], > > and Swift's `Defer statement` + resource > > close[ > https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement > ]) > > > > > > ## Description > > > > The format of the `simplified try-with-resources statement` is as > follows: > > > > ```java > > try (resources);/* Change '{}' to ';'. */ > > > > ``` > > > > It can be observed that omitting `{}` reduces the indentation, thus > > enhancing code readability by not introducing new scopes. Moreover, > > variable declarations can be positioned either before or after the > > `simplified try-with-resources statement`: > > > > ```java > > public class example1_simplified { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))); > > String studentName = "not found"; > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > Because it automatically converts the `simplified try-with-resources > > statement` to an equivalent `try-with-resources statement` after > > compilation: > > > > ```java > > public class example1_converted { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */ > > String studentName = "not found"; > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > > > System.out.println(studentName); > > }/* Add '}' before the current scope ends. */ > > } > > } > > > > ``` > > > > Because the scope of `simplified try-with-resources statement` extends > > until the current scope is terminated, the variable `studentName`, > > which originally couldn't be resolved in `example1`, can now be > > resolved successfully. This allows variable declaration, assignment, > > and usage within the same scope, ensuring that resource close > > operations occur before the current scope ends. > > > > Invoking multiple dependent `simplified try-with-resources statements` > > is similar to regular variable declarations, thereby avoiding > > excessive nesting. > > > > ```java > > public class example3_simplified { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)); > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > conn.prepareStatement(selectSQL)); > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()); > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > It will be converted to equivalent `nested try-with-resources > > statements` after compilation: > > > > ```java > > public class example3_converted { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) {/* Change ';' back to '{'. */ > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */ > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) {/* Change > > ';' back to '{'. */ > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > }}}}/* Add the corresponding number (in this case, 3) of '}' > > before the current scope ends. */ > > } > > > > ``` > > > > After code formatting: > > > > ```java > > public class example3_formatted { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > } > > } > > } > > > > ``` > > > > Comparing the code structures of `example3_simplified` and > > `example3_formatted`, it is evident that the usage of `simplified > > try-with-resources statement` enhances code readability by > > significantly reducing nested indentation levels. Additionally, it can > > be observed that in the converted `nested try-with-resources > > statements`, resource close actions will be executed in reverse order > > of declaration, just before the current scope ends. > > > > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods > > (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to > > create a simple, small-scale program: > > > > ```java > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > void main() throws SQLException { > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)); > > try (PreparedStatement statement = conn.prepareStatement(selectSQL)); > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()); > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > > > ``` > > > > > > ## Alternatives > > > > * One approach to mitigate the impact on readability caused by deeply > > `nested try-with-resources statements` is to consolidate multiple > > resources within the same `try-with-resources statement` as much as > > possible. However, formatting multiple resources with line breaks can > > also disrupt visual clarity, and furthermore, it's not always feasible > > to close many resources simultaneously: > > > > ```java > > public class alternative1 { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password); > > PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > /* In this case, there are only 2 resources, but if there are 3 > or more, > > it will become even less aesthetically pleasing. */ > > > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > /* ResultSet 'result' cannot be closed simultaneously with > > PreparedStatement 'statement' > > because PreparedStatement 'statement' needs parameter > > configuration first. */ > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > return studentName; > > } > > } > > } > > } > > > > ``` > > > > * Alternatively, creating a separate method to confine the scope of > > managed resources within the method can be done, but it necessitates > > 'naming' the new > > method[https://www.karlton.org/2017/12/naming-things-hard/]. This > > approach simply shifts complexity to the interior of additional > > method, which may not be conducive to creating simple, small-scale > > programs: > > > > ```java > > public class alternative2 { > > public static void main(String... args) throws SQLException { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > String studentName = getStudentName(selectSQL, studentID); > > > > System.out.println(studentName); > > } > > > > public static String getStudentName(String sql, String studentID) > > throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > try (PreparedStatement statement = > conn.prepareStatement(sql)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > return studentName; > > } > > } > > } > > } > > } > > > > ``` > > > > /* GET BETTER EVERY DAY */ > -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Mon Sep 25 10:06:11 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Mon, 25 Sep 2023 13:06:11 +0300 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: The discussion of improved TwR came before several times, and as always I think this is a great idea. A small problem I have with the proposal is that it breaks the semantics of "try", as you have no way to catch. I would in general prefer to have a plan for a complete new stronger enhanced auto-cleanup mechanism and only then break it into small JEPs On Mon, Sep 25, 2023, 11:18 tzengshinfu wrote: > Hi, Folks, > > Since my last suggestion, > I've conducted some research and stumbled upon the > `@Cleanup`[https://projectlombok.org/features/Cleanup] feature in the > popular library `Lombok`. > > Personally, I believe that the existence of this feature indicates a > certain demand, but it's unfortunate that it requires importing the > library, which adds to the maintenance complexity. > I'd like to hear your thoughts on this situation. Is importing Lombok > the best approach? > > P.S. This is my second email to the mail list. If there's anything I > may have overlooked or done incorrectly, please feel free to let me > know. Thank you. > > Thanks for your interest and support, > Hsinfu Tseng > > /* GET BETTER EVERY DAY */ > > > tzengshinfu ? 2023?9?4? ?? ??11:14??? > > > > Hi, Folks: > > > > Recently, I've been using `JBang` to write some script tools, and I > > feel that Java has transformed with modern syntax features like > > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly > > added language features provide developers with more options in > > specific scenarios, almost like giving Java a set of wings. > > > > When using the `try-with-resources statement`, I thought it might be > > possible to introduce an additional simplified syntax, which I believe > > would be useful when developing simple, small-scale programs. > > Meanwhile, scenarios requiring resource closure timing specification > > or exception handling can still make use of the original syntax. > > > > At first glance, this simplified syntax may seem a bit unusual, but > > considering that `switch` itself encompasses both statements and > > expressions, and that `switch > > expression`[https://openjdk.org/jeps/361] also adds a semicolon at the > > end of `}`, perhaps it won't be as difficult to accept. > > > > Because it reduces indentation, making the logic clearer and more > > visible, users don't need to consider resource closure timing; they > > can focus solely on business logic. It's possible that in the future, > > the likelihood of using this simplified syntax will increase. > > > > In my personal opinion, the original `try-with-resources statement` > > feels like driving a manual transmission car, where I have full > > control over everything. However, if we can let the runtime manage it > > autonomously, it's like driving an automatic car on the long journey > > of coding, and even more effortless. > > > > If there could be an alternative approach that is both hassle-free and > > doesn't require altering Java specifications, I hope that experienced > > individuals can share their insights. > > > > > > Thanks for your interest and support, > > Hsinfu Tseng > > > > (Using the JEP template because I've found it helps clarify my thoughts.) > > > > > > ## Summary > > > > Allow omitting the definition of scope when declaring > > `try-with-resources statement`. > > > > > > ## Goals > > > > * Not introducing new scopes to reduce the cognitive burden on > > developers, especially beginners. > > * Reducing the interference of hierarchical indentation to enhance > > code readability. > > * The declaration position of variables is not restricted by > > `try-with-resources statement`. > > * Ensure that resources are released before the current scope ends. > > > > > > ## Motivation > > > > The `try-with-resources statement` simplified resource management in > > Java after version 7. However, its side effect is the necessity to > > declare a scope and, at the same time, it restricts lifetime of the > > variable declared within the scope: > > > > ```java > > public class example1 { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) { > > String studentName = "not found"; > > /* Variable 'studentName' is only alive in the scope of > > try-with-resources statement. */ > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > } > > > > System.out.println(studentName); > > /* Variable 'studentName' cannot be resolved here. */ > > } > > } > > > > ``` > > > > While it's possible to move variable declaration before > > `try-with-resources statement` and modify variable content within the > > scope of `try-with-resources statement`, the actions of declaration, > > assignment, and usage of variables are segmented by the scope of > > `try-with-resources statement`. In practice, we would prefer variable > > declarations to be as close as possible to their usage locations and > > avoid unnecessary > > separation[https://rules.sonarsource.com/java/RSPEC-1941/]: > > > > ```java > > public class example2 { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > String studentName = "not found"; > > > > // #region > > // The scope of try-with-resources statement separates > > // the declaration, assignment, and usage of variable > 'studentName'. > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) { > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > } > > // #endregion > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > Furthermore, methods that involve multiple resources requiring > > management and dependencies might become cumbersome in terms of syntax > > due to `nested try-with-resources statements`: > > > > ```java > > public class example3 { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > } > > } > > } > > > > ``` > > > > This proposal introduces a shorthand declaration that eliminates the > > scope limitations of `try-with-resources statement`. It enables > > variable declaration, assignment, and usage within the same scope, > > reduces indentation levels for improved code readability (particularly > > in `nested try-with-resources statements`), and defers the timing of > > resource release to the system, ensuring resource disposal before the > > scope in `try-with-resources statement` is declared ends. (Similar to > > Golang's `Defer statement` + resource > > close[https://go.dev/tour/flowcontrol/12], C#'s `using > > declarations`[ > https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using > ], > > and Swift's `Defer statement` + resource > > close[ > https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement > ]) > > > > > > ## Description > > > > The format of the `simplified try-with-resources statement` is as > follows: > > > > ```java > > try (resources);/* Change '{}' to ';'. */ > > > > ``` > > > > It can be observed that omitting `{}` reduces the indentation, thus > > enhancing code readability by not introducing new scopes. Moreover, > > variable declarations can be positioned either before or after the > > `simplified try-with-resources statement`: > > > > ```java > > public class example1_simplified { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))); > > String studentName = "not found"; > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > Because it automatically converts the `simplified try-with-resources > > statement` to an equivalent `try-with-resources statement` after > > compilation: > > > > ```java > > public class example1_converted { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */ > > String studentName = "not found"; > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > > > System.out.println(studentName); > > }/* Add '}' before the current scope ends. */ > > } > > } > > > > ``` > > > > Because the scope of `simplified try-with-resources statement` extends > > until the current scope is terminated, the variable `studentName`, > > which originally couldn't be resolved in `example1`, can now be > > resolved successfully. This allows variable declaration, assignment, > > and usage within the same scope, ensuring that resource close > > operations occur before the current scope ends. > > > > Invoking multiple dependent `simplified try-with-resources statements` > > is similar to regular variable declarations, thereby avoiding > > excessive nesting. > > > > ```java > > public class example3_simplified { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)); > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > conn.prepareStatement(selectSQL)); > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()); > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > It will be converted to equivalent `nested try-with-resources > > statements` after compilation: > > > > ```java > > public class example3_converted { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) {/* Change ';' back to '{'. */ > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */ > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) {/* Change > > ';' back to '{'. */ > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > }}}}/* Add the corresponding number (in this case, 3) of '}' > > before the current scope ends. */ > > } > > > > ``` > > > > After code formatting: > > > > ```java > > public class example3_formatted { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > } > > } > > } > > > > ``` > > > > Comparing the code structures of `example3_simplified` and > > `example3_formatted`, it is evident that the usage of `simplified > > try-with-resources statement` enhances code readability by > > significantly reducing nested indentation levels. Additionally, it can > > be observed that in the converted `nested try-with-resources > > statements`, resource close actions will be executed in reverse order > > of declaration, just before the current scope ends. > > > > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods > > (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to > > create a simple, small-scale program: > > > > ```java > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > void main() throws SQLException { > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)); > > try (PreparedStatement statement = conn.prepareStatement(selectSQL)); > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()); > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > > > ``` > > > > > > ## Alternatives > > > > * One approach to mitigate the impact on readability caused by deeply > > `nested try-with-resources statements` is to consolidate multiple > > resources within the same `try-with-resources statement` as much as > > possible. However, formatting multiple resources with line breaks can > > also disrupt visual clarity, and furthermore, it's not always feasible > > to close many resources simultaneously: > > > > ```java > > public class alternative1 { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password); > > PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > /* In this case, there are only 2 resources, but if there are 3 > or more, > > it will become even less aesthetically pleasing. */ > > > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > /* ResultSet 'result' cannot be closed simultaneously with > > PreparedStatement 'statement' > > because PreparedStatement 'statement' needs parameter > > configuration first. */ > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > return studentName; > > } > > } > > } > > } > > > > ``` > > > > * Alternatively, creating a separate method to confine the scope of > > managed resources within the method can be done, but it necessitates > > 'naming' the new > > method[https://www.karlton.org/2017/12/naming-things-hard/]. This > > approach simply shifts complexity to the interior of additional > > method, which may not be conducive to creating simple, small-scale > > programs: > > > > ```java > > public class alternative2 { > > public static void main(String... args) throws SQLException { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > String studentName = getStudentName(selectSQL, studentID); > > > > System.out.println(studentName); > > } > > > > public static String getStudentName(String sql, String studentID) > > throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > try (PreparedStatement statement = > conn.prepareStatement(sql)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > return studentName; > > } > > } > > } > > } > > } > > > > ``` > > > > /* GET BETTER EVERY DAY */ > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mark.reinhold at oracle.com Mon Sep 25 12:36:50 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Mon, 25 Sep 2023 12:36:50 +0000 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) Message-ID: <20230925123648.6793D649179@eggemoggin.niobe.net> https://openjdk.org/jeps/455 Summary: Enhance pattern matching by allowing primitive type patterns to be used in all pattern contexts, align the semantics of primitive type patterns with that of instanceof, and extend switch to allow primitive constants as case labels. This is a preview language feature. - Mark From mark.reinhold at oracle.com Mon Sep 25 14:40:17 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Mon, 25 Sep 2023 14:40:17 +0000 Subject: New candidate JEP: 456: Unnamed Variables and Patterns Message-ID: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> https://openjdk.org/jeps/456 Summary: Enhance the Java language with unnamed variables, which can be initialized but not used, and unnamed patterns, which match a record component without stating the component's name or type. Both are denoted by an underscore character, _. - Mark From brian.goetz at oracle.com Mon Sep 25 16:26:16 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 25 Sep 2023 12:26:16 -0400 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: <1335398c-4638-3ed1-d421-e35b5a6bec5d@oracle.com> I'm not sure taking inspiration from Go's `defer` is a great idea here.? Yes, cleanup is a common problem in C, and defer lets you co-locate actions with their compensating cleanup actions, but Go's approach explicitly ignores scopes in favor of function boundaries. The result is that things like "extract method" refactors actually changes when cleanup side-effects can be observed. For example: ??? func f() { ??????? defer fCleanup(); ??????? for i := 0; i <= 3; i++ { ? ? ? ?? ?? defer x(i) ??????????? y(i) ??????? } ??????? // cleanups haven't happen yet ??? } Now, if we refactor: ??? func g(i int) { ? ? ??? defer x(i) ??????? y(i) ? ? } ??? func f() { ??????? defer fCleanup(); ??????? for i := 0; i <= 3; i++ { ??????????? g(i) ??????? } ??????? // loop-based cleanups have happened here, their side-effects are visible ??? } the timing of cleanup side-effects changes in a visible way.? While "most of the time" this might be OK, this behavior is an error waiting to happen, and it comes from mixing block-level scoping with function-level scoping.? I wouldn't want to introduce this same problem into Java.? (But, this isn't essential to your proposal; Swift's `defer` doesn't fall into this trap.) Java's scoping rules may result in more indentation, but it means users don't have to engage in complex reasoning about the timing of cleanup actions.? Java lets you say clearly what you mean here. Reading code is more important than writing code, and the explicit scoping makes code easier to read. > This proposal introduces a shorthand declaration that eliminates the > scope limitations of `try-with-resources statement`. It enables > variable declaration, assignment, and usage within the same scope, > reduces indentation levels for improved code readability (particularly > in `nested try-with-resources statements`), and defers the timing of > resource release to the system, ensuring resource disposal before the > scope in `try-with-resources statement` is declared ends. (Similar to > Golang's `Defer statement` + resource > close[https://go.dev/tour/flowcontrol/12], C#'s `using > declarations`[https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using], > and Swift's `Defer statement` + resource > close[https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement]) From davidalayachew at gmail.com Mon Sep 25 20:12:21 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Sep 2023 16:12:21 -0400 Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> Message-ID: Hello Mark, Thank you for posting this! Very excited to see this come out. Is this the first time a JEP has jumped from Preview to GA with only one round of Preivew? I know the default is 2 rounds, and then there are some like Sequenced Collections that don't require any preview at all. Thank you for your time and help! David Alayachew On Mon, Sep 25, 2023 at 12:25?PM Mark Reinhold wrote: > https://openjdk.org/jeps/456 > > Summary: Enhance the Java language with unnamed variables, which > can be initialized but not used, and unnamed patterns, which match a > record component without stating the component's name or type. Both are > denoted by an underscore character, _. > > - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Sep 25 20:50:50 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Sep 2023 16:50:50 -0400 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: <20230925123648.6793D649179@eggemoggin.niobe.net> References: <20230925123648.6793D649179@eggemoggin.niobe.net> Message-ID: Hello Mark, Thank you for posting this! I am shocked to see the Level of Effort set to M -- all those sharp edges makes this seem much more complex! I was very surprised to see the following snippet in the JEP. > With pattern labels involving record patterns, some > patterns are considered to be exhaustive even when they > are not unconditional. For example: > > Box> bbs = ... > switch (bbs) { > case Box(Box(String s)): ... > } > > This switch is considered exhaustive on Box> > even though the pattern Box(Box(String s)) will not match > the pathological value new Box(null), which is in the > remainder set and is handled by a synthetic default > clause that throws MatchException. > > With the introduction of primitive type patterns, we > observe that unboxing follows the same philosophy. For > example: > > Box bi = ... > switch (bi) { > case Box(int i): ... > } > > This switch is considered exhaustive on Box even > though the pattern Box(int i) will not match the > pathological value new Box(null), which is in the > remainder set. This surprises me because it feels like the analogy of Box> is a poor comparison to this. More specifically, it feels like comparing Apples and Oranges. The reason I feel that way is because I have a mental model in my head that has the following rules. * If I have a top level pattern, it does not match null. That's what case null is for. * For example, case String s, I do not expect s to be null. * Because of this, I understand why a case int i would be considered exhaustive, it seems to follow the same rules as a type pattern. * And more specifically, the rules of exhaustiveness seem to align here too. If instead of case String s, I had case Foo f, I would assume that the pattern is exhaustive if (f) can match the full domain, not including null. * If I have a nested pattern, that pattern can match null. * For example, case Box(String s), I can expect s to maybe be null. * Because of this, I do not understand why a case int i would be considered exhaustive, because it seems to break from the rules of a type pattern thus far. * To give an example, if I have record Bar(int i), and then I later refactor that to be record Bar(Integer i), I would hope that my type pattern would no longer be exhaustive. But it sounds a lot like the above passage implies it WOULD be exhaustive. I do not understand this decision. Could you help me understand the what and the why? I also want to know your response to the sharp corner I raised when it comes to refactoring primitives to and from their boxed variants. Since Valhalla is on its way (hopefully bringing with it the ability to opt-in and opt-out of nullability), it feels like this sharp corner is going to protrude even further and be even sharper. Could you address that concern too please? Thank you for your time and help! David Alayachew On Mon, Sep 25, 2023 at 10:09?AM Mark Reinhold wrote: > https://openjdk.org/jeps/455 > > Summary: Enhance pattern matching by allowing primitive type patterns > to be used in all pattern contexts, align the semantics of primitive > type patterns with that of instanceof, and extend switch to allow > primitive constants as case labels. This is a preview language feature. > > - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Sep 25 20:52:18 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Sep 2023 16:52:18 -0400 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: References: <20230925123648.6793D649179@eggemoggin.niobe.net> Message-ID: Annoying. Piper mail bungled my formatting. Resending, this time with spaces instead of tabs. Yet another reason why spaces are superior to tabs. Hello Mark, Thank you for posting this! I am shocked to see the Level of Effort set to M -- all those sharp edges makes this seem much more complex! I was very surprised to see the following snippet in the JEP. > With pattern labels involving record patterns, some > patterns are considered to be exhaustive even when they > are not unconditional. For example: > > Box> bbs = ... > switch (bbs) { > case Box(Box(String s)): ... > } > > This switch is considered exhaustive on Box> > even though the pattern Box(Box(String s)) will not match > the pathological value new Box(null), which is in the > remainder set and is handled by a synthetic default > clause that throws MatchException. > > With the introduction of primitive type patterns, we > observe that unboxing follows the same philosophy. For > example: > > Box bi = ... > switch (bi) { > case Box(int i): ... > } > > This switch is considered exhaustive on Box even > though the pattern Box(int i) will not match the > pathological value new Box(null), which is in the > remainder set. This surprises me because it feels like the analogy of Box> is a poor comparison to this. More specifically, it feels like comparing Apples and Oranges. The reason I feel that way is because I have a mental model in my head that has the following rules. * If I have a top level pattern, it does not match null. That's what case null is for. * For example, case String s, I do not expect s to be null. * Because of this, I understand why a case int i would be considered exhaustive, it seems to follow the same rules as a type pattern. * And more specifically, the rules of exhaustiveness seem to align here too. If instead of case String s, I had case Foo f, I would assume that the pattern is exhaustive if (f) can match the full domain, not including null. * If I have a nested pattern, that pattern can match null. * For example, case Box(String s), I can expect s to maybe be null. * Because of this, I do not understand why a case int i would be considered exhaustive, because it seems to break from the rules of a type pattern thus far. * To give an example, if I have record Bar(int i), and then I later refactor that to be record Bar(Integer i), I would hope that my type pattern would no longer be exhaustive. But it sounds a lot like the above passage implies it WOULD be exhaustive. I do not understand this decision. Could you help me understand the what and the why? I also want to know your response to the sharp corner I raised when it comes to refactoring primitives to and from their boxed variants. Since Valhalla is on its way (hopefully bringing with it the ability to opt-in and opt-out of nullability), it feels like this sharp corner is going to protrude even further and be even sharper. Could you address that concern too please? Thank you for your time and help! David Alayachew On Mon, Sep 25, 2023 at 4:50?PM David Alayachew wrote: > Hello Mark, > > Thank you for posting this! I am shocked to see the Level of Effort set to > M -- all those sharp edges makes this seem much more complex! > > I was very surprised to see the following snippet in the JEP. > > > > With pattern labels involving record patterns, some > > patterns are considered to be exhaustive even when they > > are not unconditional. For example: > > > > Box> bbs = ... > > switch (bbs) { > > case Box(Box(String s)): ... > > } > > > > This switch is considered exhaustive on Box> > > even though the pattern Box(Box(String s)) will not match > > the pathological value new Box(null), which is in the > > remainder set and is handled by a synthetic default > > clause that throws MatchException. > > > > With the introduction of primitive type patterns, we > > observe that unboxing follows the same philosophy. For > > example: > > > > Box bi = ... > > switch (bi) { > > case Box(int i): ... > > } > > > > This switch is considered exhaustive on Box even > > though the pattern Box(int i) will not match the > > pathological value new Box(null), which is in the > > remainder set. > > This surprises me because it feels like the analogy of Box> is > a poor comparison to this. More specifically, it feels like comparing > Apples and Oranges. > > The reason I feel that way is because I have a mental model in my head > that has the following rules. > > * If I have a top level pattern, it does not match null. That's what case > null is for. > > * For example, case String s, I do not expect s to be null. > > * Because of this, I understand why a case int i would be considered > exhaustive, it seems to follow the same rules as a type pattern. > > * And more specifically, the rules of exhaustiveness seem to align here > too. If instead of case String s, I had case Foo f, I would assume that the > pattern is exhaustive if (f) can match the full domain, not including null. > > * If I have a nested pattern, that pattern can match null. > > * For example, case Box(String s), I can expect s to maybe be null. > > * Because of this, I do not understand why a case int i would be > considered exhaustive, because it seems to break from the rules of a type > pattern thus far. > > * To give an example, if I have record Bar(int i), and then I later > refactor that to be record Bar(Integer i), I would hope that my type > pattern would no longer be exhaustive. But it sounds a lot like the above > passage implies it WOULD be exhaustive. > > I do not understand this decision. Could you help me understand the what > and the why? I also want to know your response to the sharp corner I raised > when it comes to refactoring primitives to and from their boxed variants. > Since Valhalla is on its way (hopefully bringing with it the ability to > opt-in and opt-out of nullability), it feels like this sharp corner is > going to protrude even further and be even sharper. Could you address that > concern too please? > > Thank you for your time and help! > David Alayachew > > On Mon, Sep 25, 2023 at 10:09?AM Mark Reinhold > wrote: > >> https://openjdk.org/jeps/455 >> >> Summary: Enhance pattern matching by allowing primitive type patterns >> to be used in all pattern contexts, align the semantics of primitive >> type patterns with that of instanceof, and extend switch to allow >> primitive constants as case labels. This is a preview language feature. >> >> - Mark > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Sep 25 21:02:45 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Sep 2023 17:02:45 -0400 Subject: Feature Request for Primitive Patterns (JEP 455) Message-ID: Hello Amber Dev Team, I have a feature request to be included as part of JEP 455 -- Primitive Patterns. > Since the boolean type (and its corresponding boxed type) > has only two distinct values, a switch that lists both > the true and false cases is considered exhaustive: > > boolean b = ... > switch (b) { > case true -> ... > case false -> ... > // Alternatively: case true, false -> ... > } > > It is a compile-time error for this switch to include a > default clause. I formally request that this functionality be extended to the primitive type byte. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Sep 26 00:07:08 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 25 Sep 2023 20:07:08 -0400 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: References: <20230925123648.6793D649179@eggemoggin.niobe.net> Message-ID: <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> The gap between exhaustiveness and totality (which we call "remainder") is indeed somewhat counterintuitive at first, because we would like for these two words to mean the same thing.? There were many, many discussions regarding this in Record Patterns.? This JEP extends the surprise a little bit, but only in a quantitative, not qualitative way; unboxing conversions are another place where we encounter a gap between these two similar concepts. The first part of the example (Box>) is not new to this JEP; this is how nested patterns work.? (If you think about it for long enough, you realize that the alternatives are all nearly intolerable.)? The Box example just adds one more form of remainder. Imagine it worked the way your intuition tells you it should work.? I'll start with the old case, nested record patterns. Now, suppose you have: ??? record Pair(T t, U u) { } ... ??? Pair, Pair> p = ... ??? switch (p) { ??????? case Pair(Pair(A a, B b), Pair(C c, D d)): ... ??? } Under the David rules, this switch is not exhaustive, because it matches neither Pair(null, _) or Pair(_, null).? So what cases would you have to add to the switch to satisfy the compiler's complaints of non-exhaustiveness?? Write the code, and then tell me if you want to program in that language... On 9/25/2023 4:50 PM, David Alayachew wrote: > Hello Mark, > > Thank you for posting this! I am shocked to see the Level of Effort > set to M -- all those sharp edges makes this seem much more complex! > > I was very surprised to see the following snippet in the JEP. > > > > With pattern labels involving record patterns, some > > patterns are considered to be exhaustive even when they > > are not unconditional. For example: > > > > Box> bbs = ... > > switch (bbs) { > > ? ? case Box(Box(String s)): ... > > } > > > > This switch is considered exhaustive on Box> > > even though the pattern Box(Box(String s)) will not match > > the pathological value new Box(null), which is in the > > remainder set and is handled by a synthetic default > > clause that throws MatchException. > > > > With the introduction of primitive type patterns, we > > observe that unboxing follows the same philosophy. For > > example: > > > > Box bi = ... > > switch (bi) { > > ? ? case Box(int i): ... > > } > > > > This switch is considered exhaustive on Box even > > though the pattern Box(int i) will not match the > > pathological value new Box(null), which is in the > > remainder set. > > This surprises me because it feels like the analogy of > Box> is a poor comparison to this. More specifically, it > feels like comparing Apples and Oranges. > > The reason I feel that way is because I have a mental model in my head > that has the following rules. > > * If I have a top level pattern, it does not match null. That's what > case null is for. > > ? ? * For example, case String s, I do not expect s to be null. > > ? ?* Because of this, I understand why a case int i would be > considered exhaustive, it seems to follow the same rules as a type > pattern. > > * And more specifically, the rules of exhaustiveness seem to align > here too. If instead of case String s, I had case Foo f, I would > assume that the pattern is exhaustive if (f) can match the full > domain, not including null. > > * If I have a nested pattern, that pattern can match null. > > ? ? * For example, case Box(String s), I can expect s to maybe be null. > > * Because of this, I do not understand why a case int i would be > considered exhaustive, because it seems to break from the rules of a > type pattern thus far. > > * To give an example, if I have record Bar(int i), and then I later > refactor that to be record Bar(Integer i), I would hope that my type > pattern would no longer be exhaustive. But it sounds a lot like the > above passage implies it WOULD be exhaustive. > > I do not understand this decision. Could you help me understand the > what and the why? I also want to know your response to the sharp > corner I raised when it comes to refactoring primitives to and from > their boxed variants. Since Valhalla is on its way (hopefully bringing > with it the ability to opt-in and opt-out of nullability), it feels > like this sharp corner is going to protrude even further and be even > sharper. Could you address that concern too please? > > Thank you for your time and help! > David Alayachew > > On Mon, Sep 25, 2023 at 10:09?AM Mark Reinhold > wrote: > > https://openjdk.org/jeps/455 > > ? Summary: Enhance pattern matching by allowing primitive type > patterns > ? to be used in all pattern contexts, align the semantics of primitive > ? type patterns with that of instanceof, and extend switch to allow > ? primitive constants as case labels. This is a preview language > feature. > > - Mark > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Sep 26 00:08:38 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 25 Sep 2023 20:08:38 -0400 Subject: Feature Request for Primitive Patterns (JEP 455) In-Reply-To: References: Message-ID: <5807e7b8-121e-a164-74b4-3a671507b2f2@oracle.com> We considered this for byte (and short and int and long...), but having written a lot of switches over byte, I am not sure I've encountered one that enumerates all 255 cases. If and when we have range patterns (n..m), I think the story might be different. On 9/25/2023 5:02 PM, David Alayachew wrote: > > Hello Amber Dev Team, > > I have a feature request to be included as part of JEP 455 -- > Primitive Patterns. > > > Since the boolean type (and its corresponding boxed type) > > has only two distinct values, a switch that lists both > > the true and false cases is considered exhaustive: > > > > boolean b = ... > > switch (b) { > > ? case true -> ... > > ? case false -> ... > > ? // Alternatively: case true, false -> ... > > } > > > > It is a compile-time error for this switch to include a > > default clause. > > I formally request that this functionality be extended to the > primitive type byte. > > Thank you for your time and help! > David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Sep 26 03:50:11 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Sep 2023 23:50:11 -0400 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> References: <20230925123648.6793D649179@eggemoggin.niobe.net> <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> Message-ID: Hello Brian, Thank you for your response! > Imagine it worked the way your intuition tells you it > should work. I'll start with the old case, nested record > patterns. Now, suppose you have: > > record Pair(T t, U u) { } > > ... > > Pair, Pair> p = ... > > switch (p) { > case Pair(Pair(A a, B b), Pair(C c, D d)): ... > } > > Under the David rules, this switch is not exhaustive, > because it matches neither Pair(null, _) or > Pair(_, null). So what cases would you have to add to the > switch to satisfy the compiler's complaints of > non-exhaustiveness? Write the code, and then tell me if > you want to program in that language... Excellent example lol. That clarifies it perfectly. So, the reason why we do not want to have this level of specificity required by the programmer is because it would cascade into a giant pile of writing down edge cases. Makes perfect sense. > The first part of the example (Box>) is not > new to this JEP; this is how nested patterns work. (If > you think about it for long enough, you realize that the > alternatives are all nearly intolerable.) The > Box example just adds one more form of > remainder. I always try and think these questions through before asking them, but there's only so far my brain can stretch without trying things out myself. Thank you very much. > The gap between exhaustiveness and totality (which we > call "remainder") is indeed somewhat counterintuitive at > first, because we would like for these two words to mean > the same thing. There were many, many discussions > regarding this in Record Patterns. This JEP extends the > surprise a little bit, but only in a quantitative, not > qualitative way; unboxing conversions are another place > where we encounter a gap between these two similar > concepts. That word "Remainder" reminds me now. It looks like my exact question was already answered in the "Remainder" doc from a few months ago. https://openjdk.org/projects/amber/design-notes/patterns/exhaustiveness#nested-patterns-and-sealed-types In my defense though, that doc was pretty content-dense, full of explaining how the unintuitive is better than the alternative. I guess another take away from this exchange is recognizing that what I have been looking at this entire time is called the "Remainder". Knowing that gives me a cue to start watching for nulls, as well as other possible remainders. Thank you for your time and help! David Alayachew On Mon, Sep 25, 2023 at 8:07?PM Brian Goetz wrote: > The gap between exhaustiveness and totality (which we call "remainder") is > indeed somewhat counterintuitive at first, because we would like for these > two words to mean the same thing. There were many, many discussions > regarding this in Record Patterns. This JEP extends the surprise a little > bit, but only in a quantitative, not qualitative way; unboxing conversions > are another place where we encounter a gap between these two similar > concepts. > > The first part of the example (Box>) is not new to this JEP; > this is how nested patterns work. (If you think about it for long enough, > you realize that the alternatives are all nearly intolerable.) The > Box example just adds one more form of remainder. > > Imagine it worked the way your intuition tells you it should work. I'll > start with the old case, nested record patterns. Now, suppose you have: > > record Pair(T t, U u) { } > > ... > > Pair, Pair> p = ... > > switch (p) { > case Pair(Pair(A a, B b), Pair(C c, D d)): ... > } > > Under the David rules, this switch is not exhaustive, because it matches > neither Pair(null, _) or Pair(_, null). So what cases would you have to > add to the switch to satisfy the compiler's complaints of > non-exhaustiveness? Write the code, and then tell me if you want to > program in that language... > > > > > > On 9/25/2023 4:50 PM, David Alayachew wrote: > > Hello Mark, > > Thank you for posting this! I am shocked to see the Level of Effort set to > M -- all those sharp edges makes this seem much more complex! > > I was very surprised to see the following snippet in the JEP. > > > > With pattern labels involving record patterns, some > > patterns are considered to be exhaustive even when they > > are not unconditional. For example: > > > > Box> bbs = ... > > switch (bbs) { > > case Box(Box(String s)): ... > > } > > > > This switch is considered exhaustive on Box> > > even though the pattern Box(Box(String s)) will not match > > the pathological value new Box(null), which is in the > > remainder set and is handled by a synthetic default > > clause that throws MatchException. > > > > With the introduction of primitive type patterns, we > > observe that unboxing follows the same philosophy. For > > example: > > > > Box bi = ... > > switch (bi) { > > case Box(int i): ... > > } > > > > This switch is considered exhaustive on Box even > > though the pattern Box(int i) will not match the > > pathological value new Box(null), which is in the > > remainder set. > > This surprises me because it feels like the analogy of Box> is > a poor comparison to this. More specifically, it feels like comparing > Apples and Oranges. > > The reason I feel that way is because I have a mental model in my head > that has the following rules. > > * If I have a top level pattern, it does not match null. That's what case > null is for. > > * For example, case String s, I do not expect s to be null. > > * Because of this, I understand why a case int i would be considered > exhaustive, it seems to follow the same rules as a type pattern. > > * And more specifically, the rules of exhaustiveness seem to align here > too. If instead of case String s, I had case Foo f, I would assume that the > pattern is exhaustive if (f) can match the full domain, not including null. > > * If I have a nested pattern, that pattern can match null. > > * For example, case Box(String s), I can expect s to maybe be null. > > * Because of this, I do not understand why a case int i would be > considered exhaustive, because it seems to break from the rules of a type > pattern thus far. > > * To give an example, if I have record Bar(int i), and then I later > refactor that to be record Bar(Integer i), I would hope that my type > pattern would no longer be exhaustive. But it sounds a lot like the above > passage implies it WOULD be exhaustive. > > I do not understand this decision. Could you help me understand the what > and the why? I also want to know your response to the sharp corner I raised > when it comes to refactoring primitives to and from their boxed variants. > Since Valhalla is on its way (hopefully bringing with it the ability to > opt-in and opt-out of nullability), it feels like this sharp corner is > going to protrude even further and be even sharper. Could you address that > concern too please? > > Thank you for your time and help! > David Alayachew > > On Mon, Sep 25, 2023 at 10:09?AM Mark Reinhold > wrote: > >> https://openjdk.org/jeps/455 >> >> Summary: Enhance pattern matching by allowing primitive type patterns >> to be used in all pattern contexts, align the semantics of primitive >> type patterns with that of instanceof, and extend switch to allow >> primitive constants as case labels. This is a preview language feature. >> >> - Mark > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Sep 26 03:51:49 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Sep 2023 23:51:49 -0400 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: References: <20230925123648.6793D649179@eggemoggin.niobe.net> <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> Message-ID: On a side note, are there any builds of EA Java 22 that have this implemented yet? Or is it still not available? On Mon, Sep 25, 2023 at 11:50?PM David Alayachew wrote: > Hello Brian, > > Thank you for your response! > > > Imagine it worked the way your intuition tells you it > > should work. I'll start with the old case, nested record > > patterns. Now, suppose you have: > > > > record Pair(T t, U u) { } > > > > ... > > > > Pair, Pair> p = ... > > > > switch (p) { > > case Pair(Pair(A a, B b), Pair(C c, D d)): ... > > } > > > > Under the David rules, this switch is not exhaustive, > > because it matches neither Pair(null, _) or > > Pair(_, null). So what cases would you have to add to the > > switch to satisfy the compiler's complaints of > > non-exhaustiveness? Write the code, and then tell me if > > you want to program in that language... > > Excellent example lol. That clarifies it perfectly. So, the reason why we > do not want to have this level of specificity required by the programmer is > because it would cascade into a giant pile of writing down edge cases. > Makes perfect sense. > > > The first part of the example (Box>) is not > > new to this JEP; this is how nested patterns work. (If > > you think about it for long enough, you realize that the > > alternatives are all nearly intolerable.) The > > Box example just adds one more form of > > remainder. > > I always try and think these questions through before asking them, but > there's only so far my brain can stretch without trying things out myself. > Thank you very much. > > > The gap between exhaustiveness and totality (which we > > call "remainder") is indeed somewhat counterintuitive at > > first, because we would like for these two words to mean > > the same thing. There were many, many discussions > > regarding this in Record Patterns. This JEP extends the > > surprise a little bit, but only in a quantitative, not > > qualitative way; unboxing conversions are another place > > where we encounter a gap between these two similar > > concepts. > > That word "Remainder" reminds me now. It looks like my exact question was > already answered in the "Remainder" doc from a few months ago. > > > https://openjdk.org/projects/amber/design-notes/patterns/exhaustiveness#nested-patterns-and-sealed-types > > In my defense though, that doc was pretty content-dense, full of > explaining how the unintuitive is better than the alternative. > > I guess another take away from this exchange is recognizing that what I > have been looking at this entire time is called the "Remainder". Knowing > that gives me a cue to start watching for nulls, as well as other possible > remainders. > > Thank you for your time and help! > David Alayachew > > On Mon, Sep 25, 2023 at 8:07?PM Brian Goetz > wrote: > >> The gap between exhaustiveness and totality (which we call "remainder") >> is indeed somewhat counterintuitive at first, because we would like for >> these two words to mean the same thing. There were many, many discussions >> regarding this in Record Patterns. This JEP extends the surprise a little >> bit, but only in a quantitative, not qualitative way; unboxing conversions >> are another place where we encounter a gap between these two similar >> concepts. >> >> The first part of the example (Box>) is not new to this JEP; >> this is how nested patterns work. (If you think about it for long enough, >> you realize that the alternatives are all nearly intolerable.) The >> Box example just adds one more form of remainder. >> >> Imagine it worked the way your intuition tells you it should work. I'll >> start with the old case, nested record patterns. Now, suppose you have: >> >> record Pair(T t, U u) { } >> >> ... >> >> Pair, Pair> p = ... >> >> switch (p) { >> case Pair(Pair(A a, B b), Pair(C c, D d)): ... >> } >> >> Under the David rules, this switch is not exhaustive, because it matches >> neither Pair(null, _) or Pair(_, null). So what cases would you have to >> add to the switch to satisfy the compiler's complaints of >> non-exhaustiveness? Write the code, and then tell me if you want to >> program in that language... >> >> >> >> >> >> On 9/25/2023 4:50 PM, David Alayachew wrote: >> >> Hello Mark, >> >> Thank you for posting this! I am shocked to see the Level of Effort set >> to M -- all those sharp edges makes this seem much more complex! >> >> I was very surprised to see the following snippet in the JEP. >> >> >> > With pattern labels involving record patterns, some >> > patterns are considered to be exhaustive even when they >> > are not unconditional. For example: >> > >> > Box> bbs = ... >> > switch (bbs) { >> > case Box(Box(String s)): ... >> > } >> > >> > This switch is considered exhaustive on Box> >> > even though the pattern Box(Box(String s)) will not match >> > the pathological value new Box(null), which is in the >> > remainder set and is handled by a synthetic default >> > clause that throws MatchException. >> > >> > With the introduction of primitive type patterns, we >> > observe that unboxing follows the same philosophy. For >> > example: >> > >> > Box bi = ... >> > switch (bi) { >> > case Box(int i): ... >> > } >> > >> > This switch is considered exhaustive on Box even >> > though the pattern Box(int i) will not match the >> > pathological value new Box(null), which is in the >> > remainder set. >> >> This surprises me because it feels like the analogy of Box> >> is a poor comparison to this. More specifically, it feels like comparing >> Apples and Oranges. >> >> The reason I feel that way is because I have a mental model in my head >> that has the following rules. >> >> * If I have a top level pattern, it does not match null. That's what case >> null is for. >> >> * For example, case String s, I do not expect s to be null. >> >> * Because of this, I understand why a case int i would be considered >> exhaustive, it seems to follow the same rules as a type pattern. >> >> * And more specifically, the rules of exhaustiveness seem to align here >> too. If instead of case String s, I had case Foo f, I would assume that the >> pattern is exhaustive if (f) can match the full domain, not including null. >> >> * If I have a nested pattern, that pattern can match null. >> >> * For example, case Box(String s), I can expect s to maybe be null. >> >> * Because of this, I do not understand why a case int i would be >> considered exhaustive, because it seems to break from the rules of a type >> pattern thus far. >> >> * To give an example, if I have record Bar(int i), and then I later >> refactor that to be record Bar(Integer i), I would hope that my type >> pattern would no longer be exhaustive. But it sounds a lot like the above >> passage implies it WOULD be exhaustive. >> >> I do not understand this decision. Could you help me understand the what >> and the why? I also want to know your response to the sharp corner I raised >> when it comes to refactoring primitives to and from their boxed variants. >> Since Valhalla is on its way (hopefully bringing with it the ability to >> opt-in and opt-out of nullability), it feels like this sharp corner is >> going to protrude even further and be even sharper. Could you address that >> concern too please? >> >> Thank you for your time and help! >> David Alayachew >> >> On Mon, Sep 25, 2023 at 10:09?AM Mark Reinhold >> wrote: >> >>> https://openjdk.org/jeps/455 >>> >>> Summary: Enhance pattern matching by allowing primitive type patterns >>> to be used in all pattern contexts, align the semantics of primitive >>> type patterns with that of instanceof, and extend switch to allow >>> primitive constants as case labels. This is a preview language feature. >>> >>> - Mark >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Sep 26 03:59:32 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Sep 2023 23:59:32 -0400 Subject: Feature Request for Primitive Patterns (JEP 455) In-Reply-To: <5807e7b8-121e-a164-74b4-3a671507b2f2@oracle.com> References: <5807e7b8-121e-a164-74b4-3a671507b2f2@oracle.com> Message-ID: Hello Brian, Thank you for your response! > We considered this for byte (and short and int and > long...), but having written a lot of switches over byte, > I am not sure I've encountered one that enumerates all > 255 cases. Fair point. > If and when we have range patterns (n..m), I think the > story might be different. Woah, that would be perfect! Yeah, if range patterns are coming down the pipe, I'm fine waiting for that before getting this. Thank you for your time and help! David Alayachew On Mon, Sep 25, 2023 at 8:08?PM Brian Goetz wrote: > We considered this for byte (and short and int and long...), but having > written a lot of switches over byte, I am not sure I've encountered one > that enumerates all 255 cases. > > If and when we have range patterns (n..m), I think the story might be > different. > > On 9/25/2023 5:02 PM, David Alayachew wrote: > > > Hello Amber Dev Team, > > I have a feature request to be included as part of JEP 455 -- Primitive > Patterns. > > > Since the boolean type (and its corresponding boxed type) > > has only two distinct values, a switch that lists both > > the true and false cases is considered exhaustive: > > > > boolean b = ... > > switch (b) { > > case true -> ... > > case false -> ... > > // Alternatively: case true, false -> ... > > } > > > > It is a compile-time error for this switch to include a > > default clause. > > I formally request that this functionality be extended to the primitive > type byte. > > Thank you for your time and help! > David Alayachew > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Sep 26 12:32:06 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 26 Sep 2023 14:32:06 +0200 Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> Message-ID: Hello! As we are finalizing this feature, can we squeeze in a little improvement? Namely, support enum and patterns within the same case label, provided that the patterns do not declare any variables. Like: enum X {A, B} static void test(Object obj) { switch (obj) { case String _, X.B -> System.out.println("B or String"); default -> System.out.println("other"); } } public static void main(String[] args) { Test.test("ddd"); Test.test(X.B); } Currently, such a code is not supported. Or will it be considered in future JEPs? With best regards, Tagir Valeev. On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: > > https://openjdk.org/jeps/456 > > Summary: Enhance the Java language with unnamed variables, which > can be initialized but not used, and unnamed patterns, which match a > record component without stating the component's name or type. Both are > denoted by an underscore character, _. > > - Mark From forax at univ-mlv.fr Tue Sep 26 12:35:47 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 26 Sep 2023 14:35:47 +0200 (CEST) Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> Message-ID: <1188570551.4510189.1695731747548.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "Tagir Valeev" > To: "amber-dev" > Cc: "Angelos Bimpoudis" , "amber-spec-experts" > Sent: Tuesday, September 26, 2023 2:32:06 PM > Subject: Re: New candidate JEP: 456: Unnamed Variables and Patterns > Hello! As we are finalizing this feature, can we squeeze in a little > improvement? Namely, support enum and patterns within the same case > label, provided that the patterns do not declare any variables. Like: > > enum X {A, B} > > static void test(Object obj) { > switch (obj) { > case String _, X.B -> System.out.println("B or String"); > default -> System.out.println("other"); > } > } > > public static void main(String[] args) { > Test.test("ddd"); > Test.test(X.B); > } > > Currently, such a code is not supported. Or will it be considered in > future JEPs? This is a nice improvement. > > With best regards, > Tagir Valeev. regards, R?mi > > On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: >> >> https://openjdk.org/jeps/456 >> >> Summary: Enhance the Java language with unnamed variables, which >> can be initialized but not used, and unnamed patterns, which match a >> record component without stating the component's name or type. Both are >> denoted by an underscore character, _. >> > > - Mark From mark.reinhold at oracle.com Tue Sep 26 13:01:55 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Tue, 26 Sep 2023 13:01:55 +0000 Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> Message-ID: <20230926090153.119819954@eggemoggin.niobe.net> 2023/9/25 16:12:21 -0400, davidalayachew at gmail.com: > ... > > Is this the first time a JEP has jumped from Preview to GA with only one > round of Preivew? I know the default is 2 rounds, and then there are some > like Sequenced Collections that don't require any preview at all. Yes, this is the first JEP that has done just one round of Preview. In this case, the change is so straightforward and the groundwork was laid so long ago ? starting all the way back in JDK 8 in 2014 ? that a second round of preview did not seem worthwhile. - Mark From brian.goetz at oracle.com Tue Sep 26 13:06:54 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 26 Sep 2023 09:06:54 -0400 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: References: <20230925123648.6793D649179@eggemoggin.niobe.net> <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> Message-ID: <7f8b7aae-0331-636f-ce0b-4829488468df@oracle.com> > Under the David rules, this switch is not exhaustive, > > because it matches neither Pair(null, _) or > > Pair(_, null). So what cases would you have to add to the > > switch to satisfy the compiler's complaints of > > non-exhaustiveness?? Write the code, and then tell me if > > you want to program in that language... > > Excellent example lol. That clarifies it perfectly. So, the reason why > we do not want to have this level of specificity required by the > programmer is because it would cascade into a giant pile of writing > down edge cases. Makes perfect sense. Yes.? And it is not that I am against asking users to write down the edge cases when it makes sense, but here it makes no sense.? Being explicit about the potential combinatorial explosion of edge cases will not make anyone's code better, and will more likely encourage developers to achieve totality by sticking a default clause on -- which means worse exhaustiveness checking which in turn means less reliable programs. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Sep 26 13:13:13 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 26 Sep 2023 09:13:13 -0400 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: References: <20230925123648.6793D649179@eggemoggin.niobe.net> <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> Message-ID: <9c28ccbe-0fea-f68f-d4e4-d051cf57b444@oracle.com> Where null-explicit type patterns would help is when there are legitimate (human) ambiguities about whether nulls are wanted here.? In a world where we have T!/T? type patterns, you can think of the pattern `T t` as having _inferred nullity_; it is really either T! or T?, and we determine which from context.? Where we would want explicit nullity is either when the inference chooses something that you don't want, such as: These mostly come down to things like: ??? Box b = ... ??? switch (b) { ??????? case Box(Integer i) -> ... ??????? case Box(Float f) -> ... ??????? case Box(Number! n) -> ... ??? } or where the inference feels "not obvious" and you want to be explicit. As it turns out, your first example is nonsensical: > ``` > switch (p) { > ? ? case Pair!(Pair?(A a, B b), Pair?(C c, D d)): ... > } > ``` Here, the nested pattern `Pair?(A a, B b)` makes no sense, because it is a record pattern, and a record pattern *cannot* match null (because it needs a non-null receiver on which to invoke the accessors and/or deconstructor.)? So even in a world with emotional type patterns, this pattern would be rejected by the compiler as illegal. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Sep 26 13:16:20 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 26 Sep 2023 09:16:20 -0400 Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> Message-ID: I am pretty sure I saw some spec language for this go by, but Gavin can fill in the details.? But yes, this is desirable because it should be possible to mix enums and records under a sealed type: ??? sealed interface X { } ??? enum E implements X { A; } ??? record R(int x) implements X { } ??? ... ??? switch (x) { ??????? case E.A: ... ??????? case R(int x): ... ??????? // exhaustive! ??? } There is some spec trickiness because switches on enum selectors _must_ use unqualified names historically, and this has to be rationalized. On 9/26/2023 8:32 AM, Tagir Valeev wrote: > Hello! As we are finalizing this feature, can we squeeze in a little > improvement? Namely, support enum and patterns within the same case > label, provided that the patterns do not declare any variables. Like: > > enum X {A, B} > > static void test(Object obj) { > switch (obj) { > case String _, X.B -> System.out.println("B or String"); > default -> System.out.println("other"); > } > } > > public static void main(String[] args) { > Test.test("ddd"); > Test.test(X.B); > } > > Currently, such a code is not supported. Or will it be considered in > future JEPs? > > With best regards, > Tagir Valeev. > > On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: >> https://openjdk.org/jeps/456 >> >> Summary: Enhance the Java language with unnamed variables, which >> can be initialized but not used, and unnamed patterns, which match a >> record component without stating the component's name or type. Both are >> denoted by an underscore character, _. >> >> - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Sep 26 13:52:47 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 26 Sep 2023 09:52:47 -0400 Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> Message-ID: <2822e0e8-796f-168d-4866-21b40247069b@oracle.com> I see now I answered a slightly different question :) We do support case labels for enums in pattern switches, and they participate in exhaustiveness.? But we don't currently support mixing `case pattern, constant-label` in a single case label.? This is waiting for a more comprehensive treatment of constants as patterns. On 9/26/2023 8:32 AM, Tagir Valeev wrote: > Hello! As we are finalizing this feature, can we squeeze in a little > improvement? Namely, support enum and patterns within the same case > label, provided that the patterns do not declare any variables. Like: > > enum X {A, B} > > static void test(Object obj) { > switch (obj) { > case String _, X.B -> System.out.println("B or String"); > default -> System.out.println("other"); > } > } > > public static void main(String[] args) { > Test.test("ddd"); > Test.test(X.B); > } > > Currently, such a code is not supported. Or will it be considered in > future JEPs? > > With best regards, > Tagir Valeev. > > On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: >> https://openjdk.org/jeps/456 >> >> Summary: Enhance the Java language with unnamed variables, which >> can be initialized but not used, and unnamed patterns, which match a >> record component without stating the component's name or type. Both are >> denoted by an underscore character, _. >> >> - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Tue Sep 26 10:10:56 2023 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 26 Sep 2023 12:10:56 +0200 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> References: <20230925123648.6793D649179@eggemoggin.niobe.net> <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> Message-ID: I assume this won't be an issue with the nullability JEP implemented (not sure where that is in the roadmap though)? That is, in this case we could expect things like this to work, right? ``` switch (p) { case Pair!(Pair?(A a, B b), Pair?(C c, D d)): ... } ``` or ``` switch (p) { case Pair!(Pair!(A a, B b), Pair!(C c, D d)): ... } ``` And if that is the case, then trying to be tricky now would be a waste anyway. Brian Goetz ezt ?rta (id?pont: 2023. szept. 26., K, 3:19): > The gap between exhaustiveness and totality (which we call "remainder") is > indeed somewhat counterintuitive at first, because we would like for these > two words to mean the same thing. There were many, many discussions > regarding this in Record Patterns. This JEP extends the surprise a little > bit, but only in a quantitative, not qualitative way; unboxing conversions > are another place where we encounter a gap between these two similar > concepts. > > The first part of the example (Box>) is not new to this JEP; > this is how nested patterns work. (If you think about it for long enough, > you realize that the alternatives are all nearly intolerable.) The > Box example just adds one more form of remainder. > > Imagine it worked the way your intuition tells you it should work. I'll > start with the old case, nested record patterns. Now, suppose you have: > > record Pair(T t, U u) { } > > ... > > Pair, Pair> p = ... > > switch (p) { > case Pair(Pair(A a, B b), Pair(C c, D d)): ... > } > > Under the David rules, this switch is not exhaustive, because it matches > neither Pair(null, _) or Pair(_, null). So what cases would you have to > add to the switch to satisfy the compiler's complaints of > non-exhaustiveness? Write the code, and then tell me if you want to > program in that language... > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Tue Sep 26 15:09:03 2023 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 26 Sep 2023 17:09:03 +0200 Subject: New candidate JEP: 455: Primitive types in Patterns, instanceof, and switch (Preview) In-Reply-To: <9c28ccbe-0fea-f68f-d4e4-d051cf57b444@oracle.com> References: <20230925123648.6793D649179@eggemoggin.niobe.net> <309214a1-c5d0-0947-c455-c971826fd954@oracle.com> <9c28ccbe-0fea-f68f-d4e4-d051cf57b444@oracle.com> Message-ID: > > > As it turns out, your first example is nonsensical: > > > ``` > switch (p) { > case Pair!(Pair?(A a, B b), Pair?(C c, D d)): ... > } > ``` > > > Here, the nested pattern `Pair?(A a, B b)` makes no sense, because it is a > record pattern, and a record pattern *cannot* match null (because it needs > a non-null receiver on which to invoke the accessors and/or > deconstructor.) So even in a world with emotional type patterns, this > pattern would be rejected by the compiler as illegal. > Yeah, I don't know how I missed that, because I actually considered the same thing for `case Pair?(Pair? a, Pair? b)` and realized that it makes no sense ... But then I assume if we had `Pair` declared as `record Pair(A? a, B? b) {}`, then I could use this to not match a `Pair` instance with null fields: ``` switch (p) { case Pair!(String! a, String! b): ... } ``` And writing `String` would be inferred to be `String?` in this case. -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Tue Sep 26 16:14:23 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 26 Sep 2023 16:14:23 +0000 Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: <2822e0e8-796f-168d-4866-21b40247069b@oracle.com> References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> <2822e0e8-796f-168d-4866-21b40247069b@oracle.com> Message-ID: <50EB1B68-D36E-439E-A8B3-7715744D247D@oracle.com> That?s right. We made a decision to simplify the grammar of switch labels for the JEP to only have a single pattern. However, if you use the "colon form? you can express similar code: gmb at gmb-mac src % cat Switch.java public class Switch { sealed interface X { } enum E implements X {A;} record R() implements X { } public void work(X x) { var result = switch (x) { case E.A: case R(): yield 42; // exhaustive! }; } public static void main(String[] args) { System.out.println("complete"); } } gmb at gmb-mac src % java Switch.java complete As you point out, a more uniform treatment would treat all constants as patterns and allow them all to appear in pattern lists of record patterns, for example. Working on it!! Gavin On 26 Sep 2023, at 14:52, Brian Goetz wrote: I see now I answered a slightly different question :) We do support case labels for enums in pattern switches, and they participate in exhaustiveness. But we don't currently support mixing `case pattern, constant-label` in a single case label. This is waiting for a more comprehensive treatment of constants as patterns. On 9/26/2023 8:32 AM, Tagir Valeev wrote: Hello! As we are finalizing this feature, can we squeeze in a little improvement? Namely, support enum and patterns within the same case label, provided that the patterns do not declare any variables. Like: enum X {A, B} static void test(Object obj) { switch (obj) { case String _, X.B -> System.out.println("B or String"); default -> System.out.println("other"); } } public static void main(String[] args) { Test.test("ddd"); Test.test(X.B); } Currently, such a code is not supported. Or will it be considered in future JEPs? With best regards, Tagir Valeev. On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: https://openjdk.org/jeps/456 Summary: Enhance the Java language with unnamed variables, which can be initialized but not used, and unnamed patterns, which match a record component without stating the component's name or type. Both are denoted by an underscore character, _. - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From tzengshinfu at gmail.com Wed Sep 27 05:36:44 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Wed, 27 Sep 2023 13:36:44 +0800 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: Hi, Folks: Since I previously used C# (to the extent that I consumed syntactic sugar until I almost got "syntax diabetes"), I unintentionally brought my habits into the Java world. However, I now recognize the differences in their design philosophies. In fact, I agree with Tagir's opinion. TWR (Try-With-Resources) is indeed a more explicit and reliable way of automatic resource cleanup. However, when I saw JEP 445: Unnamed Classes and Instance Main Methods, which bears similarities to C# Top-level statements, and noticed that Java chose Virtual Threads instead of async/await, I realized that "less is more, simplification is the future direction; we should make the compiler do more, and we should focus on business logic. So, wouldn't it be better if we didn't have to manage the timing of resource release?" That's the basis for my suggestion. Holo's opinion is excellent. The awkward thing is that C# chose to turn the using statement into a using declaration version, while Java chose to add resource management functionality to try-catch-finally. So, any modification would break the semantics of try, but I'm also concerned that since try-with-resources already exists, there may be little room for further changes in the automatic cleanup mechanism. I also agree with Brian's opinion. `defer` might behave unexpectedly due to different scope levels, and developers might lump logic outside of resource cleanup into it, leading to dreadful results. Instead of desiring the delayed execution effect of `defer`, I think we want "a more automated automatic cleanup mechanism" as shown below: ```java public static void main(String... args) throws FileNotFoundException, IOException { BufferedReader reader = new BufferedReader(new FileReader("studentName.txt")); String studentName = "not found"; while ((studentName = reader.readLine()) != null) { break; } System.out.println(studentName); /* The BufferedReader 'reader' will be automatically cleaned up before the method exits, even without explicit TWR indication. */ } ``` Thank you for taking the time to provide thoughtful responses from various perspectives. /* GET BETTER EVERY DAY */ tzengshinfu ? 2023?9?25? ?? ??2:10??? > > Hi, Folks, > > Since my last suggestion, > I've conducted some research and stumbled upon the > `@Cleanup`[https://projectlombok.org/features/Cleanup] feature in the > popular library `Lombok`. > > Personally, I believe that the existence of this feature indicates a > certain demand, but it's unfortunate that it requires importing the > library, which adds to the maintenance complexity. > I'd like to hear your thoughts on this situation. Is importing Lombok > the best approach? > > P.S. This is my second email to the mail list. If there's anything I > may have overlooked or done incorrectly, please feel free to let me > know. Thank you. > > Thanks for your interest and support, > Hsinfu Tseng > > /* GET BETTER EVERY DAY */ > > > tzengshinfu ? 2023?9?4? ?? ??11:14??? > > > > Hi, Folks: > > > > Recently, I've been using `JBang` to write some script tools, and I > > feel that Java has transformed with modern syntax features like > > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly > > added language features provide developers with more options in > > specific scenarios, almost like giving Java a set of wings. > > > > When using the `try-with-resources statement`, I thought it might be > > possible to introduce an additional simplified syntax, which I believe > > would be useful when developing simple, small-scale programs. > > Meanwhile, scenarios requiring resource closure timing specification > > or exception handling can still make use of the original syntax. > > > > At first glance, this simplified syntax may seem a bit unusual, but > > considering that `switch` itself encompasses both statements and > > expressions, and that `switch > > expression`[https://openjdk.org/jeps/361] also adds a semicolon at the > > end of `}`, perhaps it won't be as difficult to accept. > > > > Because it reduces indentation, making the logic clearer and more > > visible, users don't need to consider resource closure timing; they > > can focus solely on business logic. It's possible that in the future, > > the likelihood of using this simplified syntax will increase. > > > > In my personal opinion, the original `try-with-resources statement` > > feels like driving a manual transmission car, where I have full > > control over everything. However, if we can let the runtime manage it > > autonomously, it's like driving an automatic car on the long journey > > of coding, and even more effortless. > > > > If there could be an alternative approach that is both hassle-free and > > doesn't require altering Java specifications, I hope that experienced > > individuals can share their insights. > > > > > > Thanks for your interest and support, > > Hsinfu Tseng > > > > (Using the JEP template because I've found it helps clarify my thoughts.) > > > > > > ## Summary > > > > Allow omitting the definition of scope when declaring > > `try-with-resources statement`. > > > > > > ## Goals > > > > * Not introducing new scopes to reduce the cognitive burden on > > developers, especially beginners. > > * Reducing the interference of hierarchical indentation to enhance > > code readability. > > * The declaration position of variables is not restricted by > > `try-with-resources statement`. > > * Ensure that resources are released before the current scope ends. > > > > > > ## Motivation > > > > The `try-with-resources statement` simplified resource management in > > Java after version 7. However, its side effect is the necessity to > > declare a scope and, at the same time, it restricts lifetime of the > > variable declared within the scope: > > > > ```java > > public class example1 { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) { > > String studentName = "not found"; > > /* Variable 'studentName' is only alive in the scope of > > try-with-resources statement. */ > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > } > > > > System.out.println(studentName); > > /* Variable 'studentName' cannot be resolved here. */ > > } > > } > > > > ``` > > > > While it's possible to move variable declaration before > > `try-with-resources statement` and modify variable content within the > > scope of `try-with-resources statement`, the actions of declaration, > > assignment, and usage of variables are segmented by the scope of > > `try-with-resources statement`. In practice, we would prefer variable > > declarations to be as close as possible to their usage locations and > > avoid unnecessary > > separation[https://rules.sonarsource.com/java/RSPEC-1941/]: > > > > ```java > > public class example2 { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > String studentName = "not found"; > > > > // #region > > // The scope of try-with-resources statement separates > > // the declaration, assignment, and usage of variable 'studentName'. > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) { > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > } > > // #endregion > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > Furthermore, methods that involve multiple resources requiring > > management and dependencies might become cumbersome in terms of syntax > > due to `nested try-with-resources statements`: > > > > ```java > > public class example3 { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > } > > } > > } > > > > ``` > > > > This proposal introduces a shorthand declaration that eliminates the > > scope limitations of `try-with-resources statement`. It enables > > variable declaration, assignment, and usage within the same scope, > > reduces indentation levels for improved code readability (particularly > > in `nested try-with-resources statements`), and defers the timing of > > resource release to the system, ensuring resource disposal before the > > scope in `try-with-resources statement` is declared ends. (Similar to > > Golang's `Defer statement` + resource > > close[https://go.dev/tour/flowcontrol/12], C#'s `using > > declarations`[https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using], > > and Swift's `Defer statement` + resource > > close[https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement]) > > > > > > ## Description > > > > The format of the `simplified try-with-resources statement` is as follows: > > > > ```java > > try (resources);/* Change '{}' to ';'. */ > > > > ``` > > > > It can be observed that omitting `{}` reduces the indentation, thus > > enhancing code readability by not introducing new scopes. Moreover, > > variable declarations can be positioned either before or after the > > `simplified try-with-resources statement`: > > > > ```java > > public class example1_simplified { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))); > > String studentName = "not found"; > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > Because it automatically converts the `simplified try-with-resources > > statement` to an equivalent `try-with-resources statement` after > > compilation: > > > > ```java > > public class example1_converted { > > public static void main(String... args) throws > > FileNotFoundException, IOException { > > try (BufferedReader reader = new BufferedReader(new > > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */ > > String studentName = "not found"; > > > > while ((studentName = reader.readLine()) != null) { > > break; > > } > > > > System.out.println(studentName); > > }/* Add '}' before the current scope ends. */ > > } > > } > > > > ``` > > > > Because the scope of `simplified try-with-resources statement` extends > > until the current scope is terminated, the variable `studentName`, > > which originally couldn't be resolved in `example1`, can now be > > resolved successfully. This allows variable declaration, assignment, > > and usage within the same scope, ensuring that resource close > > operations occur before the current scope ends. > > > > Invoking multiple dependent `simplified try-with-resources statements` > > is similar to regular variable declarations, thereby avoiding > > excessive nesting. > > > > ```java > > public class example3_simplified { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)); > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = conn.prepareStatement(selectSQL)); > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()); > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > > > ``` > > > > It will be converted to equivalent `nested try-with-resources > > statements` after compilation: > > > > ```java > > public class example3_converted { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) {/* Change ';' back to '{'. */ > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */ > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) {/* Change > > ';' back to '{'. */ > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > }}}}/* Add the corresponding number (in this case, 3) of '}' > > before the current scope ends. */ > > } > > > > ``` > > > > After code formatting: > > > > ```java > > public class example3_formatted { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > } > > } > > } > > } > > > > ``` > > > > Comparing the code structures of `example3_simplified` and > > `example3_formatted`, it is evident that the usage of `simplified > > try-with-resources statement` enhances code readability by > > significantly reducing nested indentation levels. Additionally, it can > > be observed that in the converted `nested try-with-resources > > statements`, resource close actions will be executed in reverse order > > of declaration, just before the current scope ends. > > > > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods > > (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to > > create a simple, small-scale program: > > > > ```java > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > void main() throws SQLException { > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)); > > try (PreparedStatement statement = conn.prepareStatement(selectSQL)); > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()); > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > System.out.println(studentName); > > } > > > > ``` > > > > > > ## Alternatives > > > > * One approach to mitigate the impact on readability caused by deeply > > `nested try-with-resources statements` is to consolidate multiple > > resources within the same `try-with-resources statement` as much as > > possible. However, formatting multiple resources with line breaks can > > also disrupt visual clarity, and furthermore, it's not always feasible > > to close many resources simultaneously: > > > > ```java > > public class alternative1 { > > public static void main(String... args) throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password); > > PreparedStatement statement = > > conn.prepareStatement(selectSQL)) { > > /* In this case, there are only 2 resources, but if there are 3 or more, > > it will become even less aesthetically pleasing. */ > > > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > /* ResultSet 'result' cannot be closed simultaneously with > > PreparedStatement 'statement' > > because PreparedStatement 'statement' needs parameter > > configuration first. */ > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > return studentName; > > } > > } > > } > > } > > > > ``` > > > > * Alternatively, creating a separate method to confine the scope of > > managed resources within the method can be done, but it necessitates > > 'naming' the new > > method[https://www.karlton.org/2017/12/naming-things-hard/]. This > > approach simply shifts complexity to the interior of additional > > method, which may not be conducive to creating simple, small-scale > > programs: > > > > ```java > > public class alternative2 { > > public static void main(String... args) throws SQLException { > > String selectSQL = "selectSQL"; > > String studentID = "studentID"; > > String studentName = getStudentName(selectSQL, studentID); > > > > System.out.println(studentName); > > } > > > > public static String getStudentName(String sql, String studentID) > > throws SQLException { > > String jdbcUrl = "jdbcUrl"; > > String username = "username"; > > String password = "password"; > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > username, password)) { > > try (PreparedStatement statement = conn.prepareStatement(sql)) { > > statement.setString(1, studentID); > > > > try (ResultSet result = statement.executeQuery()) { > > String studentName = "not found"; > > > > while (result.next()) { > > studentName = result.getString(1); > > } > > > > return studentName; > > } > > } > > } > > } > > } > > > > ``` > > > > /* GET BETTER EVERY DAY */ From cboudereau at gmail.com Wed Sep 27 07:55:47 2023 From: cboudereau at gmail.com (=?UTF-8?Q?Cl=C3=A9ment_BOUDEREAU?=) Date: Wed, 27 Sep 2023 09:55:47 +0200 Subject: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: <50EB1B68-D36E-439E-A8B3-7715744D247D@oracle.com> References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> <2822e0e8-796f-168d-4866-21b40247069b@oracle.com> <50EB1B68-D36E-439E-A8B3-7715744D247D@oracle.com> Message-ID: Hi Amber dev team, Firstly, thank you for the awesome work done. I tried to use unnamed variable to simplifiy pattern matching in existing code with preview feature enable and JDK21 but ended up with runtime exception. The code compiles but unit test fails while I am actually running unit test with other preview feature. Here is the PR with the problem and temporary workaround : https://github.com/cboudereau/dataseries/pull/34 FYI, I have another pull request to integrate as soon as possible preview feature of the latest jdk21 here where everything work fine locally : https://github.com/cboudereau/dataseries/pull/24 Here is the error at runtime: [ERROR] SimpleTest.simple:17 ?? Verify Bad local variable type Exception Details: Location: io/github/cboudereau/dataseries/Union$Value.compareTo(Lio/github/cboudereau/dataseries/Union$Value;)I @297: aload Reason: Type top (current frame, locals[9]) is not assignable to reference type Current Frame: bci: @297 flags: { } locals: { 'io/github/cboudereau/dataseries/Union$Value', 'io/github/cboudereau/dataseries/Union$Value', '[Z', 'io/github/cboudereau/dataseries/Union$Value$Tuple', integer, 'io/github/cboudereau/dataseries/Union$Value$Tuple', top, top, top, top, top, 'io/github/cboudereau/dataseries/Union$Value$Fixed', 'io/github/cboudereau/dataseries/Union$Value', integer, 'io/github/cboudereau/dataseries/Union$Value', integer } stack: { } Bytecode: 0000000: 1278 c000 7a4d bb00 0c59 2a2b b700 0e59 0000010: b800 1157 4e03 3604 2c05 0454 2d15 04ba 0000020: 0017 0000 ab00 0000 0000 0014 0000 0001 0000030: 0000 0000 0000 0022 bb00 1b59 0101 b700 0000040: 1d2c 0604 54bf 2d3a 0519 052c 0704 54b6 0000050: 0020 c000 243a 0c2c 0804 5403 360d 2c10 0000060: 0604 5419 0c15 0dba 0026 0000 aa00 0000 0000070: 0000 00df ffff ffff 0000 0001 0000 00df 0000080: 0000 001c 0000 006d 1905 2c10 0704 54b6 0000090: 0027 c000 243a 0e03 360f 190e 150f ba00 00000a0: 2600 00aa 0000 002b ffff ffff 0000 0001 00000b0: 0000 002b 0000 0019 0000 0022 032c 1008 00000c0: 0454 a700 9404 2c10 0904 54a7 008b 0436 00000d0: 0d2c 100a 0454 a7ff 8d19 052c 100b 0454 00000e0: b600 27c0 0024 3a0e 2c10 0c04 5403 360f 00000f0: 190e 150f ba00 2600 00aa 0000 0000 0047 0000100: ffff ffff 0000 0001 0000 0047 0000 001b 0000110: 0000 0024 022c 100d 0454 a700 3c19 0ec0 0000120: 0001 3a0b 2c10 0e04 5419 09b4 002a 190b 0000130: b400 2ab9 002e 0200 2c10 0f04 54a7 0019 0000140: 0536 0d2c 1010 0454 a7ff 1b04 3604 2c10 0000150: 1104 54a7 fec9 2c10 1204 54ac 4ebb 001b 0000160: 592d b600 362d b700 1d2c 1013 0454 bf Exception Handler Table: bci [75, 82] => handler: 348 bci [138, 146] => handler: 348 bci [219, 227] => handler: 348 Stackmap Table: append_frame(@28,Object[#122],Object[#12],Integer) same_frame(@56) same_frame(@70) full_frame(@99,{Object[#36],Object[#36],Object[#122],Object[#12],Integer,Object[#12],Top,Top,Top,Top,Top,Top,Object[#36],Integer},{}) same_frame(@136) append_frame(@154,Object[#36],Integer) same_frame(@188) same_frame(@197) same_frame(@206) chop_frame(@217,2) append_frame(@240,Object[#36],Integer) same_frame(@276) same_frame(@285) same_frame(@320) chop_frame(@331,2) full_frame(@342,{Object[#36],Object[#36],Object[#122]},{Integer}) same_locals_1_stack_item_frame(@348,Object[#52]) On Tue, Sep 26, 2023 at 6:14?PM Gavin Bierman wrote: > That?s right. We made a decision to simplify the grammar of switch labels > for the JEP to only have a single pattern. However, if you use the "colon > form? you can express similar code: > > gmb at gmb-mac src % cat Switch.java > public class Switch { > > sealed interface X { } > > enum E implements X {A;} > > record R() implements X { } > > public void work(X x) { > var result = switch (x) { > case E.A: > case R(): yield 42; > // exhaustive! > }; > } > > public static void main(String[] args) { > System.out.println("complete"); > } > } > gmb at gmb-mac src % java Switch.java > complete > > > As you point out, a more uniform treatment would treat all constants as > patterns and allow them all to appear in pattern lists of record patterns, > for example. Working on it!! > > Gavin > > On 26 Sep 2023, at 14:52, Brian Goetz wrote: > > I see now I answered a slightly different question :) > > We do support case labels for enums in pattern switches, and they > participate in exhaustiveness. But we don't currently support mixing `case > pattern, constant-label` in a single case label. This is waiting for a > more comprehensive treatment of constants as patterns. > > On 9/26/2023 8:32 AM, Tagir Valeev wrote: > > Hello! As we are finalizing this feature, can we squeeze in a little > improvement? Namely, support enum and patterns within the same case > label, provided that the patterns do not declare any variables. Like: > > enum X {A, B} > > static void test(Object obj) { > switch (obj) { > case String _, X.B -> System.out.println("B or String"); > default -> System.out.println("other"); > } > } > > public static void main(String[] args) { > Test.test("ddd"); > Test.test(X.B); > } > > Currently, such a code is not supported. Or will it be considered in > future JEPs? > > With best regards, > Tagir Valeev. > > On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: > > https://openjdk.org/jeps/456 > > Summary: Enhance the Java language with unnamed variables, which > can be initialized but not used, and unnamed patterns, which match a > record component without stating the component's name or type. Both are > denoted by an underscore character, _. > > - Mark > > > > -- C.BOUDEREAU -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Wed Sep 27 13:33:26 2023 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Wed, 27 Sep 2023 13:33:26 +0000 Subject: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> <2822e0e8-796f-168d-4866-21b40247069b@oracle.com> <50EB1B68-D36E-439E-A8B3-7715744D247D@oracle.com> Message-ID: Hello Clement! Thanks for letting us know. I recreated a stand alone reproduction of your report and indeed I get the verification error: https://bugs.openjdk.org/browse/JDK-8317048 Looking into it. Angelos ________________________________ From: Cl?ment BOUDEREAU Sent: 27 September 2023 09:55 To: Gavin Bierman Cc: Brian Goetz ; Tagir Valeev ; amber-dev at openjdk.org ; Angelos Bimpoudis ; amber-spec-experts Subject: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns Hi Amber dev team, Firstly, thank you for the awesome work done. I tried to use unnamed variable to simplifiy pattern matching in existing code with preview feature enable and JDK21 but ended up with runtime exception. The code compiles but unit test fails while I am actually running unit test with other preview feature. Here is the PR with the problem and temporary workaround : https://github.com/cboudereau/dataseries/pull/34 FYI, I have another pull request to integrate as soon as possible preview feature of the latest jdk21 here where everything work fine locally : https://github.com/cboudereau/dataseries/pull/24 Here is the error at runtime: [ERROR] SimpleTest.simple:17 ?? Verify Bad local variable type Exception Details: Location: io/github/cboudereau/dataseries/Union$Value.compareTo(Lio/github/cboudereau/dataseries/Union$Value;)I @297: aload Reason: Type top (current frame, locals[9]) is not assignable to reference type Current Frame: bci: @297 flags: { } locals: { 'io/github/cboudereau/dataseries/Union$Value', 'io/github/cboudereau/dataseries/Union$Value', '[Z', 'io/github/cboudereau/dataseries/Union$Value$Tuple', integer, 'io/github/cboudereau/dataseries/Union$Value$Tuple', top, top, top, top, top, 'io/github/cboudereau/dataseries/Union$Value$Fixed', 'io/github/cboudereau/dataseries/Union$Value', integer, 'io/github/cboudereau/dataseries/Union$Value', integer } stack: { } Bytecode: 0000000: 1278 c000 7a4d bb00 0c59 2a2b b700 0e59 0000010: b800 1157 4e03 3604 2c05 0454 2d15 04ba 0000020: 0017 0000 ab00 0000 0000 0014 0000 0001 0000030: 0000 0000 0000 0022 bb00 1b59 0101 b700 0000040: 1d2c 0604 54bf 2d3a 0519 052c 0704 54b6 0000050: 0020 c000 243a 0c2c 0804 5403 360d 2c10 0000060: 0604 5419 0c15 0dba 0026 0000 aa00 0000 0000070: 0000 00df ffff ffff 0000 0001 0000 00df 0000080: 0000 001c 0000 006d 1905 2c10 0704 54b6 0000090: 0027 c000 243a 0e03 360f 190e 150f ba00 00000a0: 2600 00aa 0000 002b ffff ffff 0000 0001 00000b0: 0000 002b 0000 0019 0000 0022 032c 1008 00000c0: 0454 a700 9404 2c10 0904 54a7 008b 0436 00000d0: 0d2c 100a 0454 a7ff 8d19 052c 100b 0454 00000e0: b600 27c0 0024 3a0e 2c10 0c04 5403 360f 00000f0: 190e 150f ba00 2600 00aa 0000 0000 0047 0000100: ffff ffff 0000 0001 0000 0047 0000 001b 0000110: 0000 0024 022c 100d 0454 a700 3c19 0ec0 0000120: 0001 3a0b 2c10 0e04 5419 09b4 002a 190b 0000130: b400 2ab9 002e 0200 2c10 0f04 54a7 0019 0000140: 0536 0d2c 1010 0454 a7ff 1b04 3604 2c10 0000150: 1104 54a7 fec9 2c10 1204 54ac 4ebb 001b 0000160: 592d b600 362d b700 1d2c 1013 0454 bf Exception Handler Table: bci [75, 82] => handler: 348 bci [138, 146] => handler: 348 bci [219, 227] => handler: 348 Stackmap Table: append_frame(@28,Object[#122],Object[#12],Integer) same_frame(@56) same_frame(@70) full_frame(@99,{Object[#36],Object[#36],Object[#122],Object[#12],Integer,Object[#12],Top,Top,Top,Top,Top,Top,Object[#36],Integer},{}) same_frame(@136) append_frame(@154,Object[#36],Integer) same_frame(@188) same_frame(@197) same_frame(@206) chop_frame(@217,2) append_frame(@240,Object[#36],Integer) same_frame(@276) same_frame(@285) same_frame(@320) chop_frame(@331,2) full_frame(@342,{Object[#36],Object[#36],Object[#122]},{Integer}) same_locals_1_stack_item_frame(@348,Object[#52]) On Tue, Sep 26, 2023 at 6:14?PM Gavin Bierman > wrote: That?s right. We made a decision to simplify the grammar of switch labels for the JEP to only have a single pattern. However, if you use the "colon form? you can express similar code: gmb at gmb-mac src % cat Switch.java public class Switch { sealed interface X { } enum E implements X {A;} record R() implements X { } public void work(X x) { var result = switch (x) { case E.A: case R(): yield 42; // exhaustive! }; } public static void main(String[] args) { System.out.println("complete"); } } gmb at gmb-mac src % java Switch.java complete As you point out, a more uniform treatment would treat all constants as patterns and allow them all to appear in pattern lists of record patterns, for example. Working on it!! Gavin On 26 Sep 2023, at 14:52, Brian Goetz > wrote: I see now I answered a slightly different question :) We do support case labels for enums in pattern switches, and they participate in exhaustiveness. But we don't currently support mixing `case pattern, constant-label` in a single case label. This is waiting for a more comprehensive treatment of constants as patterns. On 9/26/2023 8:32 AM, Tagir Valeev wrote: Hello! As we are finalizing this feature, can we squeeze in a little improvement? Namely, support enum and patterns within the same case label, provided that the patterns do not declare any variables. Like: enum X {A, B} static void test(Object obj) { switch (obj) { case String _, X.B -> System.out.println("B or String"); default -> System.out.println("other"); } } public static void main(String[] args) { Test.test("ddd"); Test.test(X.B); } Currently, such a code is not supported. Or will it be considered in future JEPs? With best regards, Tagir Valeev. On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: https://openjdk.org/jeps/456 Summary: Enhance the Java language with unnamed variables, which can be initialized but not used, and unnamed patterns, which match a record component without stating the component's name or type. Both are denoted by an underscore character, _. - Mark -- C.BOUDEREAU -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Sep 27 13:59:35 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 27 Sep 2023 15:59:35 +0200 (CEST) Subject: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> <2822e0e8-796f-168d-4866-21b40247069b@oracle.com> <50EB1B68-D36E-439E-A8B3-7715744D247D@oracle.com> Message-ID: <1947390671.6046878.1695823175328.JavaMail.zimbra@univ-eiffel.fr> > From: "Angelos Bimpoudis" > To: "Cl?ment BOUDEREAU" , "Gavin Bierman" > > Cc: "Brian Goetz" , "Tagir Valeev" , > "amber-dev" , "amber-spec-experts" > > Sent: Wednesday, September 27, 2023 3:33:26 PM > Subject: Re: [External] : Re: New candidate JEP: 456: Unnamed Variables and > Patterns > Hello Clement! > Thanks for letting us know. I recreated a stand alone reproduction of your > report and indeed I get the verification error: [ > https://bugs.openjdk.org/browse/JDK-8317048 | > https://bugs.openjdk.org/browse/JDK-8317048 ] > Looking into it. > Angelos Hello Angelos, you can also log another bug, it seems that javac allows "final" in front of a record pattern, something which is not allowed by the grammar (and also rightfully rejected by IntelliJ). record Foo ( int x) {} public static void main ( String [] args ) { Object o = args ; switch ( o ) { case final Foo( int x) -> {} // oops ! default -> {} } } regards, R?mi > From: Cl?ment BOUDEREAU > Sent: 27 September 2023 09:55 > To: Gavin Bierman > Cc: Brian Goetz ; Tagir Valeev ; > amber-dev at openjdk.org ; Angelos Bimpoudis > ; amber-spec-experts > > Subject: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns > Hi Amber dev team, > Firstly, thank you for the awesome work done. > I tried to use unnamed variable to simplifiy pattern matching in existing code > with preview feature enable and JDK21 but ended up with runtime exception. The > code compiles but unit test fails while I am actually running unit test with > other preview feature. > Here is the PR with the problem and temporary workaround : > [ > https://urldefense.com/v3/__https://github.com/cboudereau/dataseries/pull/34__;!!ACWV5N9M2RV99hQ!IQaUoSdPFw6-k9Xl8bh66a2ULWyr2pCGqHgDBldDFjJNesjswhMSNWPshaJor8k95x4FVDPHBA2WQlMKyak3HQUSGQ$ > | https://github.com/cboudereau/dataseries/pull/34 ] > FYI, I have another pull request to integrate as soon as possible preview > feature of the latest jdk21 here where everything work fine locally : > [ > https://urldefense.com/v3/__https://github.com/cboudereau/dataseries/pull/24__;!!ACWV5N9M2RV99hQ!IQaUoSdPFw6-k9Xl8bh66a2ULWyr2pCGqHgDBldDFjJNesjswhMSNWPshaJor8k95x4FVDPHBA2WQlMKyakzNtsm5g$ > | https://github.com/cboudereau/dataseries/pull/24 ] > Here is the error at runtime: > [ERROR] SimpleTest.simple:17 ?? Verify Bad local variable type > Exception Details: > Location: > io/github/cboudereau/dataseries/Union$Value.compareTo(Lio/github/cboudereau/dataseries/Union$Value;)I > @297: aload > Reason: > Type top (current frame, locals[9]) is not assignable to reference type > Current Frame: > bci: @297 > flags: { } > locals: { 'io/github/cboudereau/dataseries/Union$Value', > 'io/github/cboudereau/dataseries/Union$Value', '[Z', > 'io/github/cboudereau/dataseries/Union$Value$Tuple', integer, > 'io/github/cboudereau/dataseries/Union$Value$Tuple', top, top, top, top, top, > 'io/github/cboudereau/dataseries/Union$Value$Fixed', > 'io/github/cboudereau/dataseries/Union$Value', integer, > 'io/github/cboudereau/dataseries/Union$Value', integer } > stack: { } > Bytecode: > 0000000: 1278 c000 7a4d bb00 0c59 2a2b b700 0e59 > 0000010: b800 1157 4e03 3604 2c05 0454 2d15 04ba > 0000020: 0017 0000 ab00 0000 0000 0014 0000 0001 > 0000030: 0000 0000 0000 0022 bb00 1b59 0101 b700 > 0000040: 1d2c 0604 54bf 2d3a 0519 052c 0704 54b6 > 0000050: 0020 c000 243a 0c2c 0804 5403 360d 2c10 > 0000060: 0604 5419 0c15 0dba 0026 0000 aa00 0000 > 0000070: 0000 00df ffff ffff 0000 0001 0000 00df > 0000080: 0000 001c 0000 006d 1905 2c10 0704 54b6 > 0000090: 0027 c000 243a 0e03 360f 190e 150f ba00 > 00000a0: 2600 00aa 0000 002b ffff ffff 0000 0001 > 00000b0: 0000 002b 0000 0019 0000 0022 032c 1008 > 00000c0: 0454 a700 9404 2c10 0904 54a7 008b 0436 > 00000d0: 0d2c 100a 0454 a7ff 8d19 052c 100b 0454 > 00000e0: b600 27c0 0024 3a0e 2c10 0c04 5403 360f > 00000f0: 190e 150f ba00 2600 00aa 0000 0000 0047 > 0000100: ffff ffff 0000 0001 0000 0047 0000 001b > 0000110: 0000 0024 022c 100d 0454 a700 3c19 0ec0 > 0000120: 0001 3a0b 2c10 0e04 5419 09b4 002a 190b > 0000130: b400 2ab9 002e 0200 2c10 0f04 54a7 0019 > 0000140: 0536 0d2c 1010 0454 a7ff 1b04 3604 2c10 > 0000150: 1104 54a7 fec9 2c10 1204 54ac 4ebb 001b > 0000160: 592d b600 362d b700 1d2c 1013 0454 bf > Exception Handler Table: > bci [75, 82] => handler: 348 > bci [138, 146] => handler: 348 > bci [219, 227] => handler: 348 > Stackmap Table: > append_frame(@28,Object[#122],Object[#12],Integer) > same_frame(@56) > same_frame(@70) > full_frame(@99,{Object[#36],Object[#36],Object[#122],Object[#12],Integer,Object[#12],Top,Top,Top,Top,Top,Top,Object[#36],Integer},{}) > same_frame(@136) > append_frame(@154,Object[#36],Integer) > same_frame(@188) > same_frame(@197) > same_frame(@206) > chop_frame(@217,2) > append_frame(@240,Object[#36],Integer) > same_frame(@276) > same_frame(@285) > same_frame(@320) > chop_frame(@331,2) > full_frame(@342,{Object[#36],Object[#36],Object[#122]},{Integer}) > same_locals_1_stack_item_frame(@348,Object[#52]) > On Tue, Sep 26, 2023 at 6:14 PM Gavin Bierman < [ > mailto:gavin.bierman at oracle.com | gavin.bierman at oracle.com ] > wrote: >> That?s right. We made a decision to simplify the grammar of switch labels for >> the JEP to only have a single pattern. However, if you use the "colon form? you >> can express similar code: >> gmb at gmb-mac src % cat Switch.java >> public class Switch { >> sealed interface X { } >> enum E implements X {A;} >> record R() implements X { } >> public void work(X x) { >> var result = switch (x) { >> case E.A: >> case R(): yield 42; >> // exhaustive! >> }; >> } >> public static void main(String[] args) { >> System.out.println("complete"); >> } >> } >> gmb at gmb-mac src % java Switch.java >> complete >> As you point out, a more uniform treatment would treat all constants as patterns >> and allow them all to appear in pattern lists of record patterns, for example. >> Working on it!! >> Gavin >>> On 26 Sep 2023, at 14:52, Brian Goetz < [ mailto:brian.goetz at oracle.com | >>> brian.goetz at oracle.com ] > wrote: >>> I see now I answered a slightly different question :) >>> We do support case labels for enums in pattern switches, and they participate in >>> exhaustiveness. But we don't currently support mixing `case pattern, >>> constant-label` in a single case label. This is waiting for a more >>> comprehensive treatment of constants as patterns. >>> On 9/26/2023 8:32 AM, Tagir Valeev wrote: >>>> Hello! As we are finalizing this feature, can we squeeze in a little >>>> improvement? Namely, support enum and patterns within the same case >>>> label, provided that the patterns do not declare any variables. Like: >>>> enum X {A, B} >>>> static void test(Object obj) { >>>> switch (obj) { >>>> case String _, X.B -> System.out.println("B or String"); >>>> default -> System.out.println("other"); >>>> } >>>> } >>>> public static void main(String[] args) { >>>> Test.test("ddd"); >>>> Test.test(X.B); >>>> } >>>> Currently, such a code is not supported. Or will it be considered in >>>> future JEPs? >>>> With best regards, >>>> Tagir Valeev. >>>> On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold [ mailto:mark.reinhold at oracle.com >>>> | ] wrote: >>>>> [ https://openjdk.org/jeps/456 | https://openjdk.org/jeps/456 ] Summary: Enhance >>>>> the Java language with unnamed variables, which >>>>> can be initialized but not used, and unnamed patterns, which match a >>>>> record component without stating the component's name or type. Both are >>>>> denoted by an underscore character, _. >>>>> - Mark > -- > C.BOUDEREAU -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Sep 27 14:03:18 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 27 Sep 2023 16:03:18 +0200 (CEST) Subject: Switch on several values Message-ID: <525180955.6049957.1695823398327.JavaMail.zimbra@univ-eiffel.fr> Hi recently Cl?ment BOUDEREAU has reported a bug on amber-dev and unrelated to that bug, taking a look to the code I've noticed this int compareTo(final Value o) { return switch (new Tuple<>(this, o)) { case Tuple, Value>(Value.Infinite _, Value.Infinite _) -> 0; case Tuple, Value>(Value.Infinite _, Value.Fixed _) -> 1; case Tuple, Value>(Value.Fixed _, Value.Infinite _) -> -1; case Tuple, Value>(Value.Fixed fst, Value.Fixed snd) -> fst.value.compareTo(snd.value); }; } Here what Cl?ment want is to match two values (here, "this" and "o") but the only way to do that is to wrap them into a pair (here named Tuple), Should we not provide a way to match several values natively ? Something like int compareTo(final Value o) { return switch (this, o) { case (Value.Infinite _, Value.Infinite _) -> 0; case (Value.Infinite _, Value.Fixed _) -> 1; case (Value.Fixed _, Value.Infinite _) -> -1; case (Value.Fixed fst, Value.Fixed snd) -> fst.value.compareTo(snd.value); }; } regards, R?mi From cboudereau at gmail.com Wed Sep 27 15:54:16 2023 From: cboudereau at gmail.com (=?UTF-8?Q?Cl=C3=A9ment_BOUDEREAU?=) Date: Wed, 27 Sep 2023 17:54:16 +0200 Subject: Switch on several values In-Reply-To: <525180955.6049957.1695823398327.JavaMail.zimbra@univ-eiffel.fr> References: <525180955.6049957.1695823398327.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hi Remi, Thank you for your feedback, this is exactly what I wanted to do. Here is a tennis kata I made using pattern matching only: https://gist.github.com/cboudereau/1f3ea7acd6d6712746c6d9f0e258deff#file-tennis-rs-L50 https://gist.github.com/cboudereau/ff891382a345ca81e8f4c6c836d9837b You can also match higher tuple cardinality. regards On Wed, Sep 27, 2023 at 4:04?PM Remi Forax wrote: > Hi recently Cl?ment BOUDEREAU has reported a bug on amber-dev and > unrelated to that bug, > taking a look to the code I've noticed this > > > int compareTo(final Value o) { > return switch (new Tuple<>(this, o)) { > case Tuple, Value>(Value.Infinite _, > Value.Infinite _) -> 0; > case Tuple, Value>(Value.Infinite _, > Value.Fixed _) -> 1; > case Tuple, Value>(Value.Fixed _, > Value.Infinite _) -> -1; > case Tuple, Value>(Value.Fixed fst, > Value.Fixed snd) -> > fst.value.compareTo(snd.value); > }; > } > > Here what Cl?ment want is to match two values (here, "this" and "o") but > the only way to do that is to wrap them into a pair (here named Tuple), > Should we not provide a way to match several values natively ? > > Something like > > int compareTo(final Value o) { > return switch (this, o) { > case (Value.Infinite _, Value.Infinite _) -> 0; > case (Value.Infinite _, Value.Fixed _) -> 1; > case (Value.Fixed _, Value.Infinite _) -> -1; > case (Value.Fixed fst, Value.Fixed snd) -> > fst.value.compareTo(snd.value); > }; > } > > regards, > R?mi > -- C.BOUDEREAU -------------- next part -------------- An HTML attachment was scrubbed... URL: From cboudereau at gmail.com Wed Sep 27 16:57:56 2023 From: cboudereau at gmail.com (=?UTF-8?Q?Cl=C3=A9ment_BOUDEREAU?=) Date: Wed, 27 Sep 2023 18:57:56 +0200 Subject: Switch on several values In-Reply-To: References: <525180955.6049957.1695823398327.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Here is the infinite/fixed code working in rust for conciseness : https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=90bc78702650e8bd12af9efdd71d969a On Wed, Sep 27, 2023, 17:54 Cl?ment BOUDEREAU wrote: > Hi Remi, > > Thank you for your feedback, this is exactly what I wanted to do. > > Here is a tennis kata I made using pattern matching only: > > https://gist.github.com/cboudereau/1f3ea7acd6d6712746c6d9f0e258deff#file-tennis-rs-L50 > https://gist.github.com/cboudereau/ff891382a345ca81e8f4c6c836d9837b > > You can also match higher tuple cardinality. > > regards > > On Wed, Sep 27, 2023 at 4:04?PM Remi Forax wrote: > >> Hi recently Cl?ment BOUDEREAU has reported a bug on amber-dev and >> unrelated to that bug, >> taking a look to the code I've noticed this >> >> >> int compareTo(final Value o) { >> return switch (new Tuple<>(this, o)) { >> case Tuple, Value>(Value.Infinite _, >> Value.Infinite _) -> 0; >> case Tuple, Value>(Value.Infinite _, >> Value.Fixed _) -> 1; >> case Tuple, Value>(Value.Fixed _, >> Value.Infinite _) -> -1; >> case Tuple, Value>(Value.Fixed fst, >> Value.Fixed snd) -> >> fst.value.compareTo(snd.value); >> }; >> } >> >> Here what Cl?ment want is to match two values (here, "this" and "o") but >> the only way to do that is to wrap them into a pair (here named Tuple), >> Should we not provide a way to match several values natively ? >> >> Something like >> >> int compareTo(final Value o) { >> return switch (this, o) { >> case (Value.Infinite _, Value.Infinite _) -> 0; >> case (Value.Infinite _, Value.Fixed _) -> 1; >> case (Value.Fixed _, Value.Infinite _) -> -1; >> case (Value.Fixed fst, Value.Fixed snd) -> >> fst.value.compareTo(snd.value); >> }; >> } >> >> regards, >> R?mi >> > > > -- > C.BOUDEREAU > -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Wed Sep 27 21:00:52 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Thu, 28 Sep 2023 00:00:52 +0300 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: Hello Tzengshinfu, Unfortunately completely automatic cleanup is not a solved problem, so "as shown bellow" can't work (the 2 main problems are fields and concurrency). The best we can hope for is to have a set of modifiers to `new` and let the compiler throw a compile time exception if we don't specify the modifier (e.g. `new scoped Buffer()`, `new manual Buffer()`, ...), but this also has problems (it is annoying). Also the problem if "fluent cleanup" is not the only problem we have with the current TwR. Because of the complexity of the subject I don't think this discussion will go anywhere in here. I believe that a separate discussion should be opened in amber-spec-experts, I also don't know the current "free hands" to work on anything in this subject, but this is a different question. On Wed, Sep 27, 2023, 11:24 tzengshinfu wrote: > Hi, Folks: > > Since I previously used C# (to the extent that I consumed syntactic > sugar until I almost got "syntax diabetes"), I unintentionally brought > my habits into the Java world. However, I now recognize the > differences in their design philosophies. > > In fact, I agree with Tagir's opinion. TWR (Try-With-Resources) is > indeed a more explicit and reliable way of automatic resource cleanup. > However, when I saw JEP 445: Unnamed Classes and Instance Main > Methods, which bears similarities to C# Top-level statements, and > noticed that Java chose Virtual Threads instead of async/await, I > realized that "less is more, simplification is the future direction; > we should make the compiler do more, and we should focus on business > logic. So, wouldn't it be better if we didn't have to manage the > timing of resource release?" That's the basis for my suggestion. > > Holo's opinion is excellent. The awkward thing is that C# chose to > turn the using statement into a using declaration version, while Java > chose to add resource management functionality to try-catch-finally. > So, any modification would break the semantics of try, but I'm also > concerned that since try-with-resources already exists, there may be > little room for further changes in the automatic cleanup mechanism. > > I also agree with Brian's opinion. `defer` might behave unexpectedly > due to different scope levels, and developers might lump logic outside > of resource cleanup into it, leading to dreadful results. Instead of > desiring the delayed execution effect of `defer`, I think we want "a > more automated automatic cleanup mechanism" as shown below: > > ```java > public static void main(String... args) throws FileNotFoundException, > IOException { > BufferedReader reader = new BufferedReader(new > FileReader("studentName.txt")); > String studentName = "not found"; > > while ((studentName = reader.readLine()) != null) { > break; > } > > System.out.println(studentName); > /* The BufferedReader 'reader' will be automatically cleaned up > before the method exits, even without explicit TWR indication. */ > } > ``` > > Thank you for taking the time to provide thoughtful responses from > various perspectives. > > > /* GET BETTER EVERY DAY */ > > > > tzengshinfu ? 2023?9?25? ?? ??2:10??? > > > > Hi, Folks, > > > > Since my last suggestion, > > I've conducted some research and stumbled upon the > > `@Cleanup`[https://projectlombok.org/features/Cleanup] feature in the > > popular library `Lombok`. > > > > Personally, I believe that the existence of this feature indicates a > > certain demand, but it's unfortunate that it requires importing the > > library, which adds to the maintenance complexity. > > I'd like to hear your thoughts on this situation. Is importing Lombok > > the best approach? > > > > P.S. This is my second email to the mail list. If there's anything I > > may have overlooked or done incorrectly, please feel free to let me > > know. Thank you. > > > > Thanks for your interest and support, > > Hsinfu Tseng > > > > /* GET BETTER EVERY DAY */ > > > > > > tzengshinfu ? 2023?9?4? ?? ??11:14??? > > > > > > Hi, Folks: > > > > > > Recently, I've been using `JBang` to write some script tools, and I > > > feel that Java has transformed with modern syntax features like > > > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly > > > added language features provide developers with more options in > > > specific scenarios, almost like giving Java a set of wings. > > > > > > When using the `try-with-resources statement`, I thought it might be > > > possible to introduce an additional simplified syntax, which I believe > > > would be useful when developing simple, small-scale programs. > > > Meanwhile, scenarios requiring resource closure timing specification > > > or exception handling can still make use of the original syntax. > > > > > > At first glance, this simplified syntax may seem a bit unusual, but > > > considering that `switch` itself encompasses both statements and > > > expressions, and that `switch > > > expression`[https://openjdk.org/jeps/361] also adds a semicolon at the > > > end of `}`, perhaps it won't be as difficult to accept. > > > > > > Because it reduces indentation, making the logic clearer and more > > > visible, users don't need to consider resource closure timing; they > > > can focus solely on business logic. It's possible that in the future, > > > the likelihood of using this simplified syntax will increase. > > > > > > In my personal opinion, the original `try-with-resources statement` > > > feels like driving a manual transmission car, where I have full > > > control over everything. However, if we can let the runtime manage it > > > autonomously, it's like driving an automatic car on the long journey > > > of coding, and even more effortless. > > > > > > If there could be an alternative approach that is both hassle-free and > > > doesn't require altering Java specifications, I hope that experienced > > > individuals can share their insights. > > > > > > > > > Thanks for your interest and support, > > > Hsinfu Tseng > > > > > > (Using the JEP template because I've found it helps clarify my > thoughts.) > > > > > > > > > ## Summary > > > > > > Allow omitting the definition of scope when declaring > > > `try-with-resources statement`. > > > > > > > > > ## Goals > > > > > > * Not introducing new scopes to reduce the cognitive burden on > > > developers, especially beginners. > > > * Reducing the interference of hierarchical indentation to enhance > > > code readability. > > > * The declaration position of variables is not restricted by > > > `try-with-resources statement`. > > > * Ensure that resources are released before the current scope ends. > > > > > > > > > ## Motivation > > > > > > The `try-with-resources statement` simplified resource management in > > > Java after version 7. However, its side effect is the necessity to > > > declare a scope and, at the same time, it restricts lifetime of the > > > variable declared within the scope: > > > > > > ```java > > > public class example1 { > > > public static void main(String... args) throws > > > FileNotFoundException, IOException { > > > try (BufferedReader reader = new BufferedReader(new > > > FileReader("studentName.txt"))) { > > > String studentName = "not found"; > > > /* Variable 'studentName' is only alive in the scope of > > > try-with-resources statement. */ > > > > > > while ((studentName = reader.readLine()) != null) { > > > break; > > > } > > > } > > > > > > System.out.println(studentName); > > > /* Variable 'studentName' cannot be resolved here. */ > > > } > > > } > > > > > > ``` > > > > > > While it's possible to move variable declaration before > > > `try-with-resources statement` and modify variable content within the > > > scope of `try-with-resources statement`, the actions of declaration, > > > assignment, and usage of variables are segmented by the scope of > > > `try-with-resources statement`. In practice, we would prefer variable > > > declarations to be as close as possible to their usage locations and > > > avoid unnecessary > > > separation[https://rules.sonarsource.com/java/RSPEC-1941/]: > > > > > > ```java > > > public class example2 { > > > public static void main(String... args) throws > > > FileNotFoundException, IOException { > > > String studentName = "not found"; > > > > > > // #region > > > // The scope of try-with-resources statement separates > > > // the declaration, assignment, and usage of variable > 'studentName'. > > > try (BufferedReader reader = new BufferedReader(new > > > FileReader("studentName.txt"))) { > > > while ((studentName = reader.readLine()) != null) { > > > break; > > > } > > > } > > > // #endregion > > > > > > System.out.println(studentName); > > > } > > > } > > > > > > ``` > > > > > > Furthermore, methods that involve multiple resources requiring > > > management and dependencies might become cumbersome in terms of syntax > > > due to `nested try-with-resources statements`: > > > > > > ```java > > > public class example3 { > > > public static void main(String... args) throws SQLException { > > > String jdbcUrl = "jdbcUrl"; > > > String username = "username"; > > > String password = "password"; > > > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > > username, password)) { > > > String selectSQL = "selectSQL"; > > > String studentID = "studentID"; > > > > > > try (PreparedStatement statement = > > > conn.prepareStatement(selectSQL)) { > > > statement.setString(1, studentID); > > > > > > try (ResultSet result = statement.executeQuery()) { > > > String studentName = "not found"; > > > > > > while (result.next()) { > > > studentName = result.getString(1); > > > } > > > > > > System.out.println(studentName); > > > } > > > } > > > } > > > } > > > } > > > > > > ``` > > > > > > This proposal introduces a shorthand declaration that eliminates the > > > scope limitations of `try-with-resources statement`. It enables > > > variable declaration, assignment, and usage within the same scope, > > > reduces indentation levels for improved code readability (particularly > > > in `nested try-with-resources statements`), and defers the timing of > > > resource release to the system, ensuring resource disposal before the > > > scope in `try-with-resources statement` is declared ends. (Similar to > > > Golang's `Defer statement` + resource > > > close[https://go.dev/tour/flowcontrol/12], C#'s `using > > > declarations`[ > https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using > ], > > > and Swift's `Defer statement` + resource > > > close[ > https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement > ]) > > > > > > > > > ## Description > > > > > > The format of the `simplified try-with-resources statement` is as > follows: > > > > > > ```java > > > try (resources);/* Change '{}' to ';'. */ > > > > > > ``` > > > > > > It can be observed that omitting `{}` reduces the indentation, thus > > > enhancing code readability by not introducing new scopes. Moreover, > > > variable declarations can be positioned either before or after the > > > `simplified try-with-resources statement`: > > > > > > ```java > > > public class example1_simplified { > > > public static void main(String... args) throws > > > FileNotFoundException, IOException { > > > try (BufferedReader reader = new BufferedReader(new > > > FileReader("studentName.txt"))); > > > String studentName = "not found"; > > > > > > while ((studentName = reader.readLine()) != null) { > > > break; > > > } > > > > > > System.out.println(studentName); > > > } > > > } > > > > > > ``` > > > > > > Because it automatically converts the `simplified try-with-resources > > > statement` to an equivalent `try-with-resources statement` after > > > compilation: > > > > > > ```java > > > public class example1_converted { > > > public static void main(String... args) throws > > > FileNotFoundException, IOException { > > > try (BufferedReader reader = new BufferedReader(new > > > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */ > > > String studentName = "not found"; > > > > > > while ((studentName = reader.readLine()) != null) { > > > break; > > > } > > > > > > System.out.println(studentName); > > > }/* Add '}' before the current scope ends. */ > > > } > > > } > > > > > > ``` > > > > > > Because the scope of `simplified try-with-resources statement` extends > > > until the current scope is terminated, the variable `studentName`, > > > which originally couldn't be resolved in `example1`, can now be > > > resolved successfully. This allows variable declaration, assignment, > > > and usage within the same scope, ensuring that resource close > > > operations occur before the current scope ends. > > > > > > Invoking multiple dependent `simplified try-with-resources statements` > > > is similar to regular variable declarations, thereby avoiding > > > excessive nesting. > > > > > > ```java > > > public class example3_simplified { > > > public static void main(String... args) throws SQLException { > > > String jdbcUrl = "jdbcUrl"; > > > String username = "username"; > > > String password = "password"; > > > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > > username, password)); > > > String selectSQL = "selectSQL"; > > > String studentID = "studentID"; > > > > > > try (PreparedStatement statement = > conn.prepareStatement(selectSQL)); > > > statement.setString(1, studentID); > > > > > > try (ResultSet result = statement.executeQuery()); > > > String studentName = "not found"; > > > > > > while (result.next()) { > > > studentName = result.getString(1); > > > } > > > > > > System.out.println(studentName); > > > } > > > } > > > > > > ``` > > > > > > It will be converted to equivalent `nested try-with-resources > > > statements` after compilation: > > > > > > ```java > > > public class example3_converted { > > > public static void main(String... args) throws SQLException { > > > String jdbcUrl = "jdbcUrl"; > > > String username = "username"; > > > String password = "password"; > > > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > > username, password)) {/* Change ';' back to '{'. */ > > > String selectSQL = "selectSQL"; > > > String studentID = "studentID"; > > > > > > try (PreparedStatement statement = > > > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */ > > > statement.setString(1, studentID); > > > > > > try (ResultSet result = statement.executeQuery()) {/* Change > > > ';' back to '{'. */ > > > String studentName = "not found"; > > > > > > while (result.next()) { > > > studentName = result.getString(1); > > > } > > > > > > System.out.println(studentName); > > > }}}}/* Add the corresponding number (in this case, 3) of '}' > > > before the current scope ends. */ > > > } > > > > > > ``` > > > > > > After code formatting: > > > > > > ```java > > > public class example3_formatted { > > > public static void main(String... args) throws SQLException { > > > String jdbcUrl = "jdbcUrl"; > > > String username = "username"; > > > String password = "password"; > > > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > > username, password)) { > > > String selectSQL = "selectSQL"; > > > String studentID = "studentID"; > > > > > > try (PreparedStatement statement = > > > conn.prepareStatement(selectSQL)) { > > > statement.setString(1, studentID); > > > > > > try (ResultSet result = statement.executeQuery()) { > > > String studentName = "not found"; > > > > > > while (result.next()) { > > > studentName = result.getString(1); > > > } > > > > > > System.out.println(studentName); > > > } > > > } > > > } > > > } > > > } > > > > > > ``` > > > > > > Comparing the code structures of `example3_simplified` and > > > `example3_formatted`, it is evident that the usage of `simplified > > > try-with-resources statement` enhances code readability by > > > significantly reducing nested indentation levels. Additionally, it can > > > be observed that in the converted `nested try-with-resources > > > statements`, resource close actions will be executed in reverse order > > > of declaration, just before the current scope ends. > > > > > > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods > > > (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to > > > create a simple, small-scale program: > > > > > > ```java > > > String jdbcUrl = "jdbcUrl"; > > > String username = "username"; > > > String password = "password"; > > > String selectSQL = "selectSQL"; > > > String studentID = "studentID"; > > > > > > void main() throws SQLException { > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > > username, password)); > > > try (PreparedStatement statement = > conn.prepareStatement(selectSQL)); > > > statement.setString(1, studentID); > > > > > > try (ResultSet result = statement.executeQuery()); > > > String studentName = "not found"; > > > > > > while (result.next()) { > > > studentName = result.getString(1); > > > } > > > > > > System.out.println(studentName); > > > } > > > > > > ``` > > > > > > > > > ## Alternatives > > > > > > * One approach to mitigate the impact on readability caused by deeply > > > `nested try-with-resources statements` is to consolidate multiple > > > resources within the same `try-with-resources statement` as much as > > > possible. However, formatting multiple resources with line breaks can > > > also disrupt visual clarity, and furthermore, it's not always feasible > > > to close many resources simultaneously: > > > > > > ```java > > > public class alternative1 { > > > public static void main(String... args) throws SQLException { > > > String jdbcUrl = "jdbcUrl"; > > > String username = "username"; > > > String password = "password"; > > > String selectSQL = "selectSQL"; > > > String studentID = "studentID"; > > > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > > username, password); > > > PreparedStatement statement = > > > conn.prepareStatement(selectSQL)) { > > > /* In this case, there are only 2 resources, but if there are > 3 or more, > > > it will become even less aesthetically pleasing. */ > > > > > > statement.setString(1, studentID); > > > > > > try (ResultSet result = statement.executeQuery()) { > > > /* ResultSet 'result' cannot be closed simultaneously with > > > PreparedStatement 'statement' > > > because PreparedStatement 'statement' needs parameter > > > configuration first. */ > > > String studentName = "not found"; > > > > > > while (result.next()) { > > > studentName = result.getString(1); > > > } > > > > > > return studentName; > > > } > > > } > > > } > > > } > > > > > > ``` > > > > > > * Alternatively, creating a separate method to confine the scope of > > > managed resources within the method can be done, but it necessitates > > > 'naming' the new > > > method[https://www.karlton.org/2017/12/naming-things-hard/]. This > > > approach simply shifts complexity to the interior of additional > > > method, which may not be conducive to creating simple, small-scale > > > programs: > > > > > > ```java > > > public class alternative2 { > > > public static void main(String... args) throws SQLException { > > > String selectSQL = "selectSQL"; > > > String studentID = "studentID"; > > > String studentName = getStudentName(selectSQL, studentID); > > > > > > System.out.println(studentName); > > > } > > > > > > public static String getStudentName(String sql, String studentID) > > > throws SQLException { > > > String jdbcUrl = "jdbcUrl"; > > > String username = "username"; > > > String password = "password"; > > > > > > try (Connection conn = DriverManager.getConnection(jdbcUrl, > > > username, password)) { > > > try (PreparedStatement statement = > conn.prepareStatement(sql)) { > > > statement.setString(1, studentID); > > > > > > try (ResultSet result = statement.executeQuery()) { > > > String studentName = "not found"; > > > > > > while (result.next()) { > > > studentName = result.getString(1); > > > } > > > > > > return studentName; > > > } > > > } > > > } > > > } > > > } > > > > > > ``` > > > > > > /* GET BETTER EVERY DAY */ > -------------- next part -------------- An HTML attachment was scrubbed... URL: From cboudereau at gmail.com Thu Sep 28 06:50:00 2023 From: cboudereau at gmail.com (=?UTF-8?Q?Cl=C3=A9ment_BOUDEREAU?=) Date: Thu, 28 Sep 2023 08:50:00 +0200 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: Hi everyone. This is a really good idea to simplify such nested code. Here is 2 points I noted, if I may. 1. About scope management, I have noted something that might help (I am not sure, since this is my second participation to JEP while I read the whole history of this mail I find this example useful) : In csharp / golang and rust, this resource management is done via scope. Usually brackets or space in fsharp. To simplify the nested try-resource in java, we might remove brackets which explicitly indicate the scope. If at the same time your declare variables combined to try-resources / using in csharp it cannot works since scope using brackets can be simplified using the one line scope feature. So if I translate this pseudo java code : ```java public class example1 { public static void main(String... args) throws FileNotFoundException, IOException { try (BufferedReader reader = new BufferedReader(new FileReader("studentName.txt"))) { String studentName = "not found"; /* Variable 'studentName' is only alive in the scope of try-with-resources statement. */ while ((studentName = reader.readLine()) != null) { break; } } System.out.println(studentName); /* Variable 'studentName' cannot be resolved here. */ } } ``` to a simplified csharp working version (autocloseable is IDisposable in csharp and using is just translated to a try finally calling dispose in the finally block) : ```csharp using System; public class C { private class D : IDisposable { public D() { } public D(IDisposable _):this() { } public void Dispose(){ } } public void M() { using (var a = new D()) using (var b = new D(a)) using (var c = new D(b)){ System.Console.WriteLine("hello world"); } } } ``` But if I try to add more statements between using, it does not work anymore because scope simplication works only for the next line: this code will not compile ```csharp using System; public class C { private class D : IDisposable { public D() { } public D(IDisposable _):this() { } public void Dispose(){ } } public void M() { using (var a = new D()) var x = "hello"; var y = "world"; using (var b = new D(a2)) using (var c = new D(b)){ System.Console.WriteLine("hello world"); } } } ``` but by using a method, only one line and RAII are well managed : ```csharp using System; public class C { private class D : IDisposable { public D() { } public D(IDisposable _):this() { } public void Dispose(){ } } private static D build(string _, string _) { return new D(); } public void M() { using (var a = new D()) using (var b = build("hello", "world")) using (var c = new D(b)){ System.Console.WriteLine("hello world"); } } } ``` 2. About execution, exception and lifetime. A problem might occur with the lifetime involving async code and resource lifetime management. I know that in csharp that by using async await does not separate async construction (using continuation like CompletableFuture) and execution of the task and it might be complex to handle resource and exception in such configuration and I am really agree with what Tzengshinfu mentioned about "less is more". I think that creating a separation thanks to the new virtual threads are closed to what as been done in rust and goland and fsharp where handling such case become trivial. So if the lifecycle is done thanks scope which requires awaiting available results inside the scope, it is not that hard to make like the code was not async from the developper point of view. I hope, I did it well, do not hesitate do give me any pointer I missed if you think that the mentioned points have been already manage. Have a good day. On Wed, Sep 27, 2023, 23:01 Holo The Sage Wolf wrote: > Hello Tzengshinfu, > Unfortunately completely automatic cleanup is not a solved problem, so "as > shown bellow" can't work (the 2 main problems are fields and concurrency). > > The best we can hope for is to have a set of modifiers to `new` and let > the compiler throw a compile time exception if we don't specify the > modifier (e.g. `new scoped Buffer()`, `new manual Buffer()`, ...), but this > also has problems (it is annoying). > > Also the problem if "fluent cleanup" is not the only problem we have with > the current TwR. > > Because of the complexity of the subject I don't think this discussion > will go anywhere in here. > I believe that a separate discussion should be opened in > amber-spec-experts, I also don't know the current "free hands" to work on > anything in this subject, but this is a different question. > > > On Wed, Sep 27, 2023, 11:24 tzengshinfu wrote: > >> Hi, Folks: >> >> Since I previously used C# (to the extent that I consumed syntactic >> sugar until I almost got "syntax diabetes"), I unintentionally brought >> my habits into the Java world. However, I now recognize the >> differences in their design philosophies. >> >> In fact, I agree with Tagir's opinion. TWR (Try-With-Resources) is >> indeed a more explicit and reliable way of automatic resource cleanup. >> However, when I saw JEP 445: Unnamed Classes and Instance Main >> Methods, which bears similarities to C# Top-level statements, and >> noticed that Java chose Virtual Threads instead of async/await, I >> realized that "less is more, simplification is the future direction; >> we should make the compiler do more, and we should focus on business >> logic. So, wouldn't it be better if we didn't have to manage the >> timing of resource release?" That's the basis for my suggestion. >> >> Holo's opinion is excellent. The awkward thing is that C# chose to >> turn the using statement into a using declaration version, while Java >> chose to add resource management functionality to try-catch-finally. >> So, any modification would break the semantics of try, but I'm also >> concerned that since try-with-resources already exists, there may be >> little room for further changes in the automatic cleanup mechanism. >> >> I also agree with Brian's opinion. `defer` might behave unexpectedly >> due to different scope levels, and developers might lump logic outside >> of resource cleanup into it, leading to dreadful results. Instead of >> desiring the delayed execution effect of `defer`, I think we want "a >> more automated automatic cleanup mechanism" as shown below: >> >> ```java >> public static void main(String... args) throws FileNotFoundException, >> IOException { >> BufferedReader reader = new BufferedReader(new >> FileReader("studentName.txt")); >> String studentName = "not found"; >> >> while ((studentName = reader.readLine()) != null) { >> break; >> } >> >> System.out.println(studentName); >> /* The BufferedReader 'reader' will be automatically cleaned up >> before the method exits, even without explicit TWR indication. */ >> } >> ``` >> >> Thank you for taking the time to provide thoughtful responses from >> various perspectives. >> >> >> /* GET BETTER EVERY DAY */ >> >> >> >> tzengshinfu ? 2023?9?25? ?? ??2:10??? >> > >> > Hi, Folks, >> > >> > Since my last suggestion, >> > I've conducted some research and stumbled upon the >> > `@Cleanup`[https://projectlombok.org/features/Cleanup] feature in the >> > popular library `Lombok`. >> > >> > Personally, I believe that the existence of this feature indicates a >> > certain demand, but it's unfortunate that it requires importing the >> > library, which adds to the maintenance complexity. >> > I'd like to hear your thoughts on this situation. Is importing Lombok >> > the best approach? >> > >> > P.S. This is my second email to the mail list. If there's anything I >> > may have overlooked or done incorrectly, please feel free to let me >> > know. Thank you. >> > >> > Thanks for your interest and support, >> > Hsinfu Tseng >> > >> > /* GET BETTER EVERY DAY */ >> > >> > >> > tzengshinfu ? 2023?9?4? ?? ??11:14??? >> > > >> > > Hi, Folks: >> > > >> > > Recently, I've been using `JBang` to write some script tools, and I >> > > feel that Java has transformed with modern syntax features like >> > > `Records`, `Switch Expressions`, and `Pattern Matching`. These newly >> > > added language features provide developers with more options in >> > > specific scenarios, almost like giving Java a set of wings. >> > > >> > > When using the `try-with-resources statement`, I thought it might be >> > > possible to introduce an additional simplified syntax, which I believe >> > > would be useful when developing simple, small-scale programs. >> > > Meanwhile, scenarios requiring resource closure timing specification >> > > or exception handling can still make use of the original syntax. >> > > >> > > At first glance, this simplified syntax may seem a bit unusual, but >> > > considering that `switch` itself encompasses both statements and >> > > expressions, and that `switch >> > > expression`[https://openjdk.org/jeps/361] also adds a semicolon at >> the >> > > end of `}`, perhaps it won't be as difficult to accept. >> > > >> > > Because it reduces indentation, making the logic clearer and more >> > > visible, users don't need to consider resource closure timing; they >> > > can focus solely on business logic. It's possible that in the future, >> > > the likelihood of using this simplified syntax will increase. >> > > >> > > In my personal opinion, the original `try-with-resources statement` >> > > feels like driving a manual transmission car, where I have full >> > > control over everything. However, if we can let the runtime manage it >> > > autonomously, it's like driving an automatic car on the long journey >> > > of coding, and even more effortless. >> > > >> > > If there could be an alternative approach that is both hassle-free and >> > > doesn't require altering Java specifications, I hope that experienced >> > > individuals can share their insights. >> > > >> > > >> > > Thanks for your interest and support, >> > > Hsinfu Tseng >> > > >> > > (Using the JEP template because I've found it helps clarify my >> thoughts.) >> > > >> > > >> > > ## Summary >> > > >> > > Allow omitting the definition of scope when declaring >> > > `try-with-resources statement`. >> > > >> > > >> > > ## Goals >> > > >> > > * Not introducing new scopes to reduce the cognitive burden on >> > > developers, especially beginners. >> > > * Reducing the interference of hierarchical indentation to enhance >> > > code readability. >> > > * The declaration position of variables is not restricted by >> > > `try-with-resources statement`. >> > > * Ensure that resources are released before the current scope ends. >> > > >> > > >> > > ## Motivation >> > > >> > > The `try-with-resources statement` simplified resource management in >> > > Java after version 7. However, its side effect is the necessity to >> > > declare a scope and, at the same time, it restricts lifetime of the >> > > variable declared within the scope: >> > > >> > > ```java >> > > public class example1 { >> > > public static void main(String... args) throws >> > > FileNotFoundException, IOException { >> > > try (BufferedReader reader = new BufferedReader(new >> > > FileReader("studentName.txt"))) { >> > > String studentName = "not found"; >> > > /* Variable 'studentName' is only alive in the scope of >> > > try-with-resources statement. */ >> > > >> > > while ((studentName = reader.readLine()) != null) { >> > > break; >> > > } >> > > } >> > > >> > > System.out.println(studentName); >> > > /* Variable 'studentName' cannot be resolved here. */ >> > > } >> > > } >> > > >> > > ``` >> > > >> > > While it's possible to move variable declaration before >> > > `try-with-resources statement` and modify variable content within the >> > > scope of `try-with-resources statement`, the actions of declaration, >> > > assignment, and usage of variables are segmented by the scope of >> > > `try-with-resources statement`. In practice, we would prefer variable >> > > declarations to be as close as possible to their usage locations and >> > > avoid unnecessary >> > > separation[https://rules.sonarsource.com/java/RSPEC-1941/]: >> > > >> > > ```java >> > > public class example2 { >> > > public static void main(String... args) throws >> > > FileNotFoundException, IOException { >> > > String studentName = "not found"; >> > > >> > > // #region >> > > // The scope of try-with-resources statement separates >> > > // the declaration, assignment, and usage of variable >> 'studentName'. >> > > try (BufferedReader reader = new BufferedReader(new >> > > FileReader("studentName.txt"))) { >> > > while ((studentName = reader.readLine()) != null) { >> > > break; >> > > } >> > > } >> > > // #endregion >> > > >> > > System.out.println(studentName); >> > > } >> > > } >> > > >> > > ``` >> > > >> > > Furthermore, methods that involve multiple resources requiring >> > > management and dependencies might become cumbersome in terms of syntax >> > > due to `nested try-with-resources statements`: >> > > >> > > ```java >> > > public class example3 { >> > > public static void main(String... args) throws SQLException { >> > > String jdbcUrl = "jdbcUrl"; >> > > String username = "username"; >> > > String password = "password"; >> > > >> > > try (Connection conn = DriverManager.getConnection(jdbcUrl, >> > > username, password)) { >> > > String selectSQL = "selectSQL"; >> > > String studentID = "studentID"; >> > > >> > > try (PreparedStatement statement = >> > > conn.prepareStatement(selectSQL)) { >> > > statement.setString(1, studentID); >> > > >> > > try (ResultSet result = statement.executeQuery()) { >> > > String studentName = "not found"; >> > > >> > > while (result.next()) { >> > > studentName = result.getString(1); >> > > } >> > > >> > > System.out.println(studentName); >> > > } >> > > } >> > > } >> > > } >> > > } >> > > >> > > ``` >> > > >> > > This proposal introduces a shorthand declaration that eliminates the >> > > scope limitations of `try-with-resources statement`. It enables >> > > variable declaration, assignment, and usage within the same scope, >> > > reduces indentation levels for improved code readability (particularly >> > > in `nested try-with-resources statements`), and defers the timing of >> > > resource release to the system, ensuring resource disposal before the >> > > scope in `try-with-resources statement` is declared ends. (Similar to >> > > Golang's `Defer statement` + resource >> > > close[https://go.dev/tour/flowcontrol/12], C#'s `using >> > > declarations`[ >> https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using >> ], >> > > and Swift's `Defer statement` + resource >> > > close[ >> https://docs.swift.org/swift-book/documentation/the-swift-programming-language/statements/#Defer-Statement >> ]) >> > > >> > > >> > > ## Description >> > > >> > > The format of the `simplified try-with-resources statement` is as >> follows: >> > > >> > > ```java >> > > try (resources);/* Change '{}' to ';'. */ >> > > >> > > ``` >> > > >> > > It can be observed that omitting `{}` reduces the indentation, thus >> > > enhancing code readability by not introducing new scopes. Moreover, >> > > variable declarations can be positioned either before or after the >> > > `simplified try-with-resources statement`: >> > > >> > > ```java >> > > public class example1_simplified { >> > > public static void main(String... args) throws >> > > FileNotFoundException, IOException { >> > > try (BufferedReader reader = new BufferedReader(new >> > > FileReader("studentName.txt"))); >> > > String studentName = "not found"; >> > > >> > > while ((studentName = reader.readLine()) != null) { >> > > break; >> > > } >> > > >> > > System.out.println(studentName); >> > > } >> > > } >> > > >> > > ``` >> > > >> > > Because it automatically converts the `simplified try-with-resources >> > > statement` to an equivalent `try-with-resources statement` after >> > > compilation: >> > > >> > > ```java >> > > public class example1_converted { >> > > public static void main(String... args) throws >> > > FileNotFoundException, IOException { >> > > try (BufferedReader reader = new BufferedReader(new >> > > FileReader("studentName.txt"))) {/* Change ';' back to '{'. */ >> > > String studentName = "not found"; >> > > >> > > while ((studentName = reader.readLine()) != null) { >> > > break; >> > > } >> > > >> > > System.out.println(studentName); >> > > }/* Add '}' before the current scope ends. */ >> > > } >> > > } >> > > >> > > ``` >> > > >> > > Because the scope of `simplified try-with-resources statement` extends >> > > until the current scope is terminated, the variable `studentName`, >> > > which originally couldn't be resolved in `example1`, can now be >> > > resolved successfully. This allows variable declaration, assignment, >> > > and usage within the same scope, ensuring that resource close >> > > operations occur before the current scope ends. >> > > >> > > Invoking multiple dependent `simplified try-with-resources statements` >> > > is similar to regular variable declarations, thereby avoiding >> > > excessive nesting. >> > > >> > > ```java >> > > public class example3_simplified { >> > > public static void main(String... args) throws SQLException { >> > > String jdbcUrl = "jdbcUrl"; >> > > String username = "username"; >> > > String password = "password"; >> > > >> > > try (Connection conn = DriverManager.getConnection(jdbcUrl, >> > > username, password)); >> > > String selectSQL = "selectSQL"; >> > > String studentID = "studentID"; >> > > >> > > try (PreparedStatement statement = >> conn.prepareStatement(selectSQL)); >> > > statement.setString(1, studentID); >> > > >> > > try (ResultSet result = statement.executeQuery()); >> > > String studentName = "not found"; >> > > >> > > while (result.next()) { >> > > studentName = result.getString(1); >> > > } >> > > >> > > System.out.println(studentName); >> > > } >> > > } >> > > >> > > ``` >> > > >> > > It will be converted to equivalent `nested try-with-resources >> > > statements` after compilation: >> > > >> > > ```java >> > > public class example3_converted { >> > > public static void main(String... args) throws SQLException { >> > > String jdbcUrl = "jdbcUrl"; >> > > String username = "username"; >> > > String password = "password"; >> > > >> > > try (Connection conn = DriverManager.getConnection(jdbcUrl, >> > > username, password)) {/* Change ';' back to '{'. */ >> > > String selectSQL = "selectSQL"; >> > > String studentID = "studentID"; >> > > >> > > try (PreparedStatement statement = >> > > conn.prepareStatement(selectSQL)) {/* Change ';' back to '{'. */ >> > > statement.setString(1, studentID); >> > > >> > > try (ResultSet result = statement.executeQuery()) {/* Change >> > > ';' back to '{'. */ >> > > String studentName = "not found"; >> > > >> > > while (result.next()) { >> > > studentName = result.getString(1); >> > > } >> > > >> > > System.out.println(studentName); >> > > }}}}/* Add the corresponding number (in this case, 3) of '}' >> > > before the current scope ends. */ >> > > } >> > > >> > > ``` >> > > >> > > After code formatting: >> > > >> > > ```java >> > > public class example3_formatted { >> > > public static void main(String... args) throws SQLException { >> > > String jdbcUrl = "jdbcUrl"; >> > > String username = "username"; >> > > String password = "password"; >> > > >> > > try (Connection conn = DriverManager.getConnection(jdbcUrl, >> > > username, password)) { >> > > String selectSQL = "selectSQL"; >> > > String studentID = "studentID"; >> > > >> > > try (PreparedStatement statement = >> > > conn.prepareStatement(selectSQL)) { >> > > statement.setString(1, studentID); >> > > >> > > try (ResultSet result = statement.executeQuery()) { >> > > String studentName = "not found"; >> > > >> > > while (result.next()) { >> > > studentName = result.getString(1); >> > > } >> > > >> > > System.out.println(studentName); >> > > } >> > > } >> > > } >> > > } >> > > } >> > > >> > > ``` >> > > >> > > Comparing the code structures of `example3_simplified` and >> > > `example3_formatted`, it is evident that the usage of `simplified >> > > try-with-resources statement` enhances code readability by >> > > significantly reducing nested indentation levels. Additionally, it can >> > > be observed that in the converted `nested try-with-resources >> > > statements`, resource close actions will be executed in reverse order >> > > of declaration, just before the current scope ends. >> > > >> > > If coupled with `JEP 445: Unnamed Classes and Instance Main Methods >> > > (Preview)`[https://openjdk.org/jeps/445], it becomes convenient to >> > > create a simple, small-scale program: >> > > >> > > ```java >> > > String jdbcUrl = "jdbcUrl"; >> > > String username = "username"; >> > > String password = "password"; >> > > String selectSQL = "selectSQL"; >> > > String studentID = "studentID"; >> > > >> > > void main() throws SQLException { >> > > try (Connection conn = DriverManager.getConnection(jdbcUrl, >> > > username, password)); >> > > try (PreparedStatement statement = >> conn.prepareStatement(selectSQL)); >> > > statement.setString(1, studentID); >> > > >> > > try (ResultSet result = statement.executeQuery()); >> > > String studentName = "not found"; >> > > >> > > while (result.next()) { >> > > studentName = result.getString(1); >> > > } >> > > >> > > System.out.println(studentName); >> > > } >> > > >> > > ``` >> > > >> > > >> > > ## Alternatives >> > > >> > > * One approach to mitigate the impact on readability caused by deeply >> > > `nested try-with-resources statements` is to consolidate multiple >> > > resources within the same `try-with-resources statement` as much as >> > > possible. However, formatting multiple resources with line breaks can >> > > also disrupt visual clarity, and furthermore, it's not always feasible >> > > to close many resources simultaneously: >> > > >> > > ```java >> > > public class alternative1 { >> > > public static void main(String... args) throws SQLException { >> > > String jdbcUrl = "jdbcUrl"; >> > > String username = "username"; >> > > String password = "password"; >> > > String selectSQL = "selectSQL"; >> > > String studentID = "studentID"; >> > > >> > > try (Connection conn = DriverManager.getConnection(jdbcUrl, >> > > username, password); >> > > PreparedStatement statement = >> > > conn.prepareStatement(selectSQL)) { >> > > /* In this case, there are only 2 resources, but if there are >> 3 or more, >> > > it will become even less aesthetically pleasing. */ >> > > >> > > statement.setString(1, studentID); >> > > >> > > try (ResultSet result = statement.executeQuery()) { >> > > /* ResultSet 'result' cannot be closed simultaneously with >> > > PreparedStatement 'statement' >> > > because PreparedStatement 'statement' needs parameter >> > > configuration first. */ >> > > String studentName = "not found"; >> > > >> > > while (result.next()) { >> > > studentName = result.getString(1); >> > > } >> > > >> > > return studentName; >> > > } >> > > } >> > > } >> > > } >> > > >> > > ``` >> > > >> > > * Alternatively, creating a separate method to confine the scope of >> > > managed resources within the method can be done, but it necessitates >> > > 'naming' the new >> > > method[https://www.karlton.org/2017/12/naming-things-hard/]. This >> > > approach simply shifts complexity to the interior of additional >> > > method, which may not be conducive to creating simple, small-scale >> > > programs: >> > > >> > > ```java >> > > public class alternative2 { >> > > public static void main(String... args) throws SQLException { >> > > String selectSQL = "selectSQL"; >> > > String studentID = "studentID"; >> > > String studentName = getStudentName(selectSQL, studentID); >> > > >> > > System.out.println(studentName); >> > > } >> > > >> > > public static String getStudentName(String sql, String studentID) >> > > throws SQLException { >> > > String jdbcUrl = "jdbcUrl"; >> > > String username = "username"; >> > > String password = "password"; >> > > >> > > try (Connection conn = DriverManager.getConnection(jdbcUrl, >> > > username, password)) { >> > > try (PreparedStatement statement = >> conn.prepareStatement(sql)) { >> > > statement.setString(1, studentID); >> > > >> > > try (ResultSet result = statement.executeQuery()) { >> > > String studentName = "not found"; >> > > >> > > while (result.next()) { >> > > studentName = result.getString(1); >> > > } >> > > >> > > return studentName; >> > > } >> > > } >> > > } >> > > } >> > > } >> > > >> > > ``` >> > > >> > > /* GET BETTER EVERY DAY */ >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Fri Sep 29 09:32:57 2023 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Fri, 29 Sep 2023 09:32:57 +0000 Subject: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns In-Reply-To: <1947390671.6046878.1695823175328.JavaMail.zimbra@univ-eiffel.fr> References: <20230925144015.B4B3C6491DB@eggemoggin.niobe.net> <2822e0e8-796f-168d-4866-21b40247069b@oracle.com> <50EB1B68-D36E-439E-A8B3-7715744D247D@oracle.com> <1947390671.6046878.1695823175328.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Thank you, Remi, we are on it! ? https://bugs.openjdk.org/browse/JDK-8317300 ________________________________ From: Remi Forax Sent: 27 September 2023 15:59 To: Angelos Bimpoudis Cc: Cl?ment BOUDEREAU ; Gavin Bierman ; Brian Goetz ; Tagir Valeev ; amber-dev ; amber-spec-experts Subject: Re: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns ________________________________ From: "Angelos Bimpoudis" To: "Cl?ment BOUDEREAU" , "Gavin Bierman" Cc: "Brian Goetz" , "Tagir Valeev" , "amber-dev" , "amber-spec-experts" Sent: Wednesday, September 27, 2023 3:33:26 PM Subject: Re: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns Hello Clement! Thanks for letting us know. I recreated a stand alone reproduction of your report and indeed I get the verification error: https://bugs.openjdk.org/browse/JDK-8317048 Looking into it. Angelos Hello Angelos, you can also log another bug, it seems that javac allows "final" in front of a record pattern, something which is not allowed by the grammar (and also rightfully rejected by IntelliJ). record Foo(int x) {} public static void main(String[] args) { Object o = args; switch(o) { case final Foo(int x) -> {} // oops ! default -> {} } } regards, R?mi ________________________________ From: Cl?ment BOUDEREAU Sent: 27 September 2023 09:55 To: Gavin Bierman Cc: Brian Goetz ; Tagir Valeev ; amber-dev at openjdk.org ; Angelos Bimpoudis ; amber-spec-experts Subject: [External] : Re: New candidate JEP: 456: Unnamed Variables and Patterns Hi Amber dev team, Firstly, thank you for the awesome work done. I tried to use unnamed variable to simplifiy pattern matching in existing code with preview feature enable and JDK21 but ended up with runtime exception. The code compiles but unit test fails while I am actually running unit test with other preview feature. Here is the PR with the problem and temporary workaround : https://github.com/cboudereau/dataseries/pull/34 FYI, I have another pull request to integrate as soon as possible preview feature of the latest jdk21 here where everything work fine locally : https://github.com/cboudereau/dataseries/pull/24 Here is the error at runtime: [ERROR] SimpleTest.simple:17 ?? Verify Bad local variable type Exception Details: Location: io/github/cboudereau/dataseries/Union$Value.compareTo(Lio/github/cboudereau/dataseries/Union$Value;)I @297: aload Reason: Type top (current frame, locals[9]) is not assignable to reference type Current Frame: bci: @297 flags: { } locals: { 'io/github/cboudereau/dataseries/Union$Value', 'io/github/cboudereau/dataseries/Union$Value', '[Z', 'io/github/cboudereau/dataseries/Union$Value$Tuple', integer, 'io/github/cboudereau/dataseries/Union$Value$Tuple', top, top, top, top, top, 'io/github/cboudereau/dataseries/Union$Value$Fixed', 'io/github/cboudereau/dataseries/Union$Value', integer, 'io/github/cboudereau/dataseries/Union$Value', integer } stack: { } Bytecode: 0000000: 1278 c000 7a4d bb00 0c59 2a2b b700 0e59 0000010: b800 1157 4e03 3604 2c05 0454 2d15 04ba 0000020: 0017 0000 ab00 0000 0000 0014 0000 0001 0000030: 0000 0000 0000 0022 bb00 1b59 0101 b700 0000040: 1d2c 0604 54bf 2d3a 0519 052c 0704 54b6 0000050: 0020 c000 243a 0c2c 0804 5403 360d 2c10 0000060: 0604 5419 0c15 0dba 0026 0000 aa00 0000 0000070: 0000 00df ffff ffff 0000 0001 0000 00df 0000080: 0000 001c 0000 006d 1905 2c10 0704 54b6 0000090: 0027 c000 243a 0e03 360f 190e 150f ba00 00000a0: 2600 00aa 0000 002b ffff ffff 0000 0001 00000b0: 0000 002b 0000 0019 0000 0022 032c 1008 00000c0: 0454 a700 9404 2c10 0904 54a7 008b 0436 00000d0: 0d2c 100a 0454 a7ff 8d19 052c 100b 0454 00000e0: b600 27c0 0024 3a0e 2c10 0c04 5403 360f 00000f0: 190e 150f ba00 2600 00aa 0000 0000 0047 0000100: ffff ffff 0000 0001 0000 0047 0000 001b 0000110: 0000 0024 022c 100d 0454 a700 3c19 0ec0 0000120: 0001 3a0b 2c10 0e04 5419 09b4 002a 190b 0000130: b400 2ab9 002e 0200 2c10 0f04 54a7 0019 0000140: 0536 0d2c 1010 0454 a7ff 1b04 3604 2c10 0000150: 1104 54a7 fec9 2c10 1204 54ac 4ebb 001b 0000160: 592d b600 362d b700 1d2c 1013 0454 bf Exception Handler Table: bci [75, 82] => handler: 348 bci [138, 146] => handler: 348 bci [219, 227] => handler: 348 Stackmap Table: append_frame(@28,Object[#122],Object[#12],Integer) same_frame(@56) same_frame(@70) full_frame(@99,{Object[#36],Object[#36],Object[#122],Object[#12],Integer,Object[#12],Top,Top,Top,Top,Top,Top,Object[#36],Integer},{}) same_frame(@136) append_frame(@154,Object[#36],Integer) same_frame(@188) same_frame(@197) same_frame(@206) chop_frame(@217,2) append_frame(@240,Object[#36],Integer) same_frame(@276) same_frame(@285) same_frame(@320) chop_frame(@331,2) full_frame(@342,{Object[#36],Object[#36],Object[#122]},{Integer}) same_locals_1_stack_item_frame(@348,Object[#52]) On Tue, Sep 26, 2023 at 6:14?PM Gavin Bierman > wrote: That?s right. We made a decision to simplify the grammar of switch labels for the JEP to only have a single pattern. However, if you use the "colon form? you can express similar code: gmb at gmb-mac src % cat Switch.java public class Switch { sealed interface X { } enum E implements X {A;} record R() implements X { } public void work(X x) { var result = switch (x) { case E.A: case R(): yield 42; // exhaustive! }; } public static void main(String[] args) { System.out.println("complete"); } } gmb at gmb-mac src % java Switch.java complete As you point out, a more uniform treatment would treat all constants as patterns and allow them all to appear in pattern lists of record patterns, for example. Working on it!! Gavin On 26 Sep 2023, at 14:52, Brian Goetz > wrote: I see now I answered a slightly different question :) We do support case labels for enums in pattern switches, and they participate in exhaustiveness. But we don't currently support mixing `case pattern, constant-label` in a single case label. This is waiting for a more comprehensive treatment of constants as patterns. On 9/26/2023 8:32 AM, Tagir Valeev wrote: Hello! As we are finalizing this feature, can we squeeze in a little improvement? Namely, support enum and patterns within the same case label, provided that the patterns do not declare any variables. Like: enum X {A, B} static void test(Object obj) { switch (obj) { case String _, X.B -> System.out.println("B or String"); default -> System.out.println("other"); } } public static void main(String[] args) { Test.test("ddd"); Test.test(X.B); } Currently, such a code is not supported. Or will it be considered in future JEPs? With best regards, Tagir Valeev. On Mon, Sep 25, 2023 at 6:25?PM Mark Reinhold wrote: https://openjdk.org/jeps/456 Summary: Enhance the Java language with unnamed variables, which can be initialized but not used, and unnamed patterns, which match a record component without stating the component's name or type. Both are denoted by an underscore character, _. - Mark -- C.BOUDEREAU -------------- next part -------------- An HTML attachment was scrubbed... URL: