From tzengshinfu at gmail.com Mon Oct 2 07:46:15 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Mon, 2 Oct 2023 15:46:15 +0800 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: Hi, Holo, Concurrency, as you mentioned, is indeed a challenging topic. However, personally, I find new scoped/manual Buffer() much cuter compared to indentation. Unfortunately, it didn't exist in the Java world from the beginning. Sometimes, I use IIFE (Immediately Invoked Function Expressions) to "hide" TWR, but it's somewhat like not tidying up a room and just closing the door... ```java public class example1 { public static void main(String... args) throws FileNotFoundException, IOException { String studentName = ((Function) path -> { String result = "not found"; try (BufferedReader reader = new BufferedReader(new FileReader(path))) { while ((result = reader.readLine()) != null) { break; } } catch (final FileNotFoundException ex) { } catch (final IOException ex) { } return result; }).apply("studentName.txt"); System.out.println(studentName); } } ``` By the way, Swift's IIFE is quite elegant. How can an IIFE in a strongly typed language look so good? /* GET BETTER EVERY DAY */ Holo The Sage Wolf ? 2023?9?28? ?? ??5:01??? > > 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 */ From tzengshinfu at gmail.com Mon Oct 2 08:07:48 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Mon, 2 Oct 2023 16:07:48 +0800 Subject: Idea[Simplified try-with-resources statement] In-Reply-To: References: Message-ID: Hi, Cl?ment, Thank you for your response. In C#, most of the time, I use `Using Declaration`, to be honest, it has saved me quite a few brain cells. ```C# 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"); } } } ``` /* GET BETTER EVERY DAY */ Cl?ment BOUDEREAU ? 2023?9?28? ?? ??2:50??? > > 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 */ From mark.reinhold at oracle.com Thu Oct 5 12:20:38 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Thu, 5 Oct 2023 12:20:38 +0000 Subject: New candidate JEP: 459: String Templates (Second Preview) Message-ID: <20231005122036.AAED264A0FB@eggemoggin.niobe.net> https://openjdk.org/jeps/459 Summary: Enhance the Java programming language with string templates. String templates complement Java's existing string literals and text blocks by coupling literal text with embedded expressions and template processors to produce specialized results. This is a preview language feature and API. - Mark From james.laskey at oracle.com Thu Oct 5 12:24:23 2023 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 5 Oct 2023 12:24:23 +0000 Subject: New candidate JEP: 459: String Templates (Second Preview) In-Reply-To: <20231005122036.AAED264A0FB@eggemoggin.niobe.net> References: <20231005122036.AAED264A0FB@eggemoggin.niobe.net> Message-ID: <5393F773-1144-4B4F-AC9B-039F75602548@oracle.com> Thank you Mark. > On Oct 5, 2023, at 9:20 AM, Mark Reinhold wrote: > > https://openjdk.org/jeps/459 > > Summary: Enhance the Java programming language with string > templates. String templates complement Java's existing string literals > and text blocks by coupling literal text with embedded expressions and > template processors to produce specialized results. This is a preview > language feature and API. > > - Mark From davidalayachew at gmail.com Thu Oct 5 12:44:10 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 5 Oct 2023 08:44:10 -0400 Subject: New candidate JEP: 459: String Templates (Second Preview) In-Reply-To: <20231005122036.AAED264A0FB@eggemoggin.niobe.net> References: <20231005122036.AAED264A0FB@eggemoggin.niobe.net> Message-ID: Hello Mark, Thank you for sending the notice! In the JEP, it says we are going another round to gather experience and feedback, but with no changes. Out of curiosity, for JEP's like this where another round of preview is done, but no changes have been made, what sort of feedback is wanted that wasn't provided last time? Is it a quantity thing, where they just want more people to respond before they send this out to the wild? Or do they want more involved feedback from someone using this feature in a non-trivial example? Or is it more of a sixth-sense/feel-it-out sort of thing? Thank you for your time! David Alayachew On Thu, Oct 5, 2023 at 8:20?AM Mark Reinhold wrote: > https://openjdk.org/jeps/459 > > Summary: Enhance the Java programming language with string > templates. String templates complement Java's existing string literals > and text blocks by coupling literal text with embedded expressions and > template processors to produce specialized results. This is a preview > language feature and API. > > - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From mark.reinhold at oracle.com Mon Oct 9 13:03:13 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Mon, 9 Oct 2023 13:03:13 +0000 Subject: New candidate JEP: 459: String Templates (Second Preview) In-Reply-To: References: <20231005122036.AAED264A0FB@eggemoggin.niobe.net> Message-ID: <20231009090310.172118700@eggemoggin.niobe.net> 2023/10/5 8:44:10 -0400, davidalayachew at gmail.com: > In the JEP, it says we are going another round to gather experience and > feedback, but with no changes. > > Out of curiosity, for JEP's like this where another round of preview is > done, but no changes have been made, what sort of feedback is wanted that > wasn't provided last time? Is it a quantity thing, where they just want > more people to respond before they send this out to the wild? Or do they > want more involved feedback from someone using this feature in a > non-trivial example? Or is it more of a sixth-sense/feel-it-out sort of > thing? https://openjdk.org/jeps/12#Readiness https://openjdk.org/jeps/12#Re-preview - Mark From davidalayachew at gmail.com Mon Oct 9 18:13:41 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 9 Oct 2023 14:13:41 -0400 Subject: New candidate JEP: 459: String Templates (Second Preview) In-Reply-To: <20231009090310.172118700@eggemoggin.niobe.net> References: <20231005122036.AAED264A0FB@eggemoggin.niobe.net> <20231009090310.172118700@eggemoggin.niobe.net> Message-ID: Hello, Thank you for your response! Very insightful, especially about the 3 months and stabilization. Makes a lot more sense how the 2 rounds of preview became the norm. That definitely answers my question. Also, I appreciate you putting in linking tags like that. That makes these JEP's easier to talk about and dissect. Some of them can be very content dense (like the JEP 12 lol). The more linking tags, the better. Thank you for your time and help! David Alayachew On Mon, Oct 9, 2023, 9:03 AM Mark Reinhold wrote: > 2023/10/5 8:44:10 -0400, davidalayachew at gmail.com: > > In the JEP, it says we are going another round to gather experience and > > feedback, but with no changes. > > > > Out of curiosity, for JEP's like this where another round of preview is > > done, but no changes have been made, what sort of feedback is wanted that > > wasn't provided last time? Is it a quantity thing, where they just want > > more people to respond before they send this out to the wild? Or do they > > want more involved feedback from someone using this feature in a > > non-trivial example? Or is it more of a sixth-sense/feel-it-out sort of > > thing? > > https://openjdk.org/jeps/12#Readiness > https://openjdk.org/jeps/12#Re-preview > > - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From steffenyount at gmail.com Wed Oct 11 23:25:59 2023 From: steffenyount at gmail.com (Steffen Yount) Date: Wed, 11 Oct 2023 16:25:59 -0700 Subject: Bridging the divide between unconditionalitty and exhaustiveness for ReferenceType patterns Message-ID: Amber devs, For the Patterns available to us in Java 21 it has been noted that: 1) TypePatterns match unconditionally, that is to say they are "null matching". Meanwhile RecordPatterns at most match exhaustively leaving null valued ReferenceTypes in the remainder, that is to say RecordPatterns are "non-null matching" or "instance matching". 2) It's the "null hostility" of the "switch" and "instanceof" operators that prevents TypePatterns from matching/dominating the null values there: * In the case of "switch" a null valued ReferenceType selector expression will cause a NullPointerException to be thrown by the switch operator unless the "case null" switch label has been declared in its switch block. * In the case of "instanceof" evaluation, when the the RelationalExpression is null valued, null is interpreted semantically as "not an instance" and thus also as "not an instance of" the ReferenceType (or Pattern) found to the right of the "instanceof" keyword. This dichotomy between TypePatterns and RecordPatterns illustrates a gap in current pattern matching coverage wherein we cannot match null vs non-null vs nullable for non-record ReferenceTypes. I would love to see this gap closed. In JEP 455, I can see there's a lot of work going into making Patterns work well with PrimitiveTypes, is anyone looking into making Patterns work better with these ReferenceTypes? What are your thoughts on closing this pattern matching coverage gap and maybe implementing something like the following: The current Java 21 Pattern definitions look like Pattern: TypePattern RecordPattern The TypePattern part could be deconstructed and extended giving new Pattern definitions that look more like: Pattern: PrimitivePattern (PrimitiveType) RecordPattern (non-null valued record ReferenceType) InstancePattern (non-null valued ReferenceType) NullPattern (null valued ReferenceType) ReferencePattern (ReferenceType) Notes: 1) The InstancePattern matching a ReferenceType T and the NullPattern matching that same ReferenceType T are complements of each other and exhaustively cover the match space of a ReferencePattern matching that same ReferenceType T 2) The ReferencePattern matching a ReferenceType T dominates both a NullPattern and a InstancePattern matching that same ReferenceType T 3) The RecordPattern matching a record ReferenceType T for a Record declared with a zero-arg constructor exhaustively covers the InstancePattern matching that same ReferenceType T 4) The InstancePattern matching a record ReferenceType T dominates a RecordPattern matching that same ReferenceType T Sample of proposed Pattern notation: match-only Pattern notation: PrimitivePattern: int RecordPattern: Bat(_,_) InstancePattern: Bat() NullPattern: null ReferencePattern: Bat match+projection Pattern notation: PrimitivePattern: int ii RecordPattern: Bat(_,_) bb InstancePattern: Bat() bb NullPattern: null bb ReferencePattern: Bat bb (When matched, the pattern's named variables are projected into the appropriate declaration scope) Proposed Pattern appearance and idiomatic familiarity: PrimitivePattern: Resembles a local PrimitiveType variable declaration RecordPattern: Resembles an "all-args" constructor call for a record ReferenceType InstancePattern: Resembles a "no-args" constructor call for a ReferenceType NullPattern: Resembles a ReferencePattern where the ReferenceType has been replaced with the NullLiteral ReferencePattern: Resembles a local ReferenceType variable declaration Does this notation feel good and Java-ish to you? Have you already got something better figured out? Your thoughts? Thanks for your consideration, -Steffen -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Oct 12 13:22:16 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 12 Oct 2023 09:22:16 -0400 Subject: Bridging the divide between unconditionalitty and exhaustiveness for ReferenceType patterns In-Reply-To: References: Message-ID: Backing up, what you're saying here is that both total and partial reference type patterns have opinions about nullity (null-accepting and null-rejecting, respectively), and while these may be reasonable defaults, you would like to be able to "override" these defaults.? I sympathize. There was much discussion about whether we needed a separate way of saying something like (T|Null), or T-but-not-null, explicitly.? As you can imagine, many syntaxes were proposed, but they were all unsatisfying because (a) they ceded much syntactic real estate (and therefore much mindshare) to a distinction that was small, and often ignorable, and (b) if we ever got nullity control in the future (T! and T?), these ad-hoc pattern forms would look permanently silly. On 10/11/2023 7:25 PM, Steffen Yount wrote: > Amber devs, > > For the Patterns available to us in Java 21 it has been noted that: > 1) TypePatterns match unconditionally, that is to say they are "null > matching". Meanwhile RecordPatterns at most match exhaustively leaving > null valued ReferenceTypes in the remainder, that is to say > RecordPatterns are "non-null matching" or "instance matching". > > 2) It's the "null hostility" of the "switch" and "instanceof" > operators that prevents TypePatterns from matching/dominating the null > values there: > > * In the case of "switch" a null valued ReferenceType selector > expression will cause a NullPointerException to be thrown by the > ?switch operator unless the "case null" switch label has been declared > in its switch block. > > * In the case of "instanceof" evaluation, when the the > RelationalExpression is null valued, null is interpreted semantically > as "not an instance" and thus also as "not an instance of" the > ReferenceType (or Pattern) found to the right of the "instanceof" keyword. > > > This dichotomy between TypePatterns and RecordPatterns illustrates a > gap in current pattern matching coverage wherein we cannot match null > vs non-null vs nullable for non-record ReferenceTypes. > > I would love to see this gap closed. > > In JEP 455, I can see there's a lot of work going into making Patterns > work well with PrimitiveTypes, is anyone looking into making?Patterns > work better with these ReferenceTypes? > > What are your thoughts on closing this pattern matching coverage gap > and maybe implementing something like the following: > > The current Java 21 Pattern definitions look like > > Pattern: > ? ? TypePattern > ? ? RecordPattern > > The TypePattern part could be deconstructed and extended giving new > Pattern definitions that look more like: > > Pattern: > ? ? PrimitivePattern (PrimitiveType) > ? ? RecordPattern ? ?(non-null valued record ReferenceType) > ? ? InstancePattern ?(non-null valued ReferenceType) > ? ? NullPattern ? ? ?(null valued ReferenceType) > ? ? ReferencePattern (ReferenceType) > > Notes: > 1) The InstancePattern matching a ReferenceType T and the NullPattern > matching that same ReferenceType T are complements of each other and > exhaustively cover the match space of a ReferencePattern matching that > same ReferenceType T > > 2) The ReferencePattern matching a ReferenceType T dominates both a > NullPattern and a InstancePattern matching that same ReferenceType T > > 3) The RecordPattern matching a record ReferenceType T for a Record > declared with a zero-arg constructor exhaustively covers the > InstancePattern matching that same ReferenceType T > > 4) The InstancePattern matching a record ReferenceType T dominates a > RecordPattern matching that same ReferenceType T > > Sample of proposed Pattern notation: > > match-only Pattern notation: > ? ? PrimitivePattern: int > ? ? RecordPattern: ? ?Bat(_,_) > ? ? InstancePattern: ?Bat() > ? ? NullPattern: ? ? ?null > ? ? ReferencePattern: Bat > > match+projection Pattern notation: > ? ? PrimitivePattern: int ii > ? ? RecordPattern: ? ?Bat(_,_) bb > ? ? InstancePattern: ?Bat() bb > ? ? NullPattern: ? ? ?null bb > ? ? ReferencePattern: Bat bb > > (When matched, the pattern's named variables are projected into the > appropriate declaration scope) > > Proposed Pattern appearance and idiomatic familiarity: > ? ? PrimitivePattern: Resembles a local PrimitiveType variable declaration > ? ? RecordPattern: ? ?Resembles an "all-args" constructor call for a > record ReferenceType > ? ? InstancePattern: ?Resembles a "no-args" constructor call for a > ReferenceType > ? ? NullPattern: ? ? ?Resembles a ReferencePattern where the > ReferenceType has been replaced with the NullLiteral > ? ? ReferencePattern: Resembles a local ReferenceType variable declaration > > Does this notation feel good and Java-ish to you? Have you already got > something better figured out? Your thoughts? > > Thanks for your consideration, > -Steffen -------------- next part -------------- An HTML attachment was scrubbed... URL: From ella.ananeva at oracle.com Wed Oct 18 02:04:47 2023 From: ella.ananeva at oracle.com (Ella Ananeva) Date: Wed, 18 Oct 2023 02:04:47 +0000 Subject: A question on JEP 455 specification Message-ID: Hi team, Reading the spec for JEP 455, I was puzzled with the following part: The following conversions are unconditionally exact: * ? * a widening primitive conversion which is: * from any integral type to any integral type and neither of them is char * ?. * ? At first (at the first 5 times, I confess) I read it as ?an unconditionally exact conversion is, among others, a widening primitive conversion, e.g. a conversion from any integral type to any integral type, but not char.? I understand now that what was likely meant is that an unconditionally exact conversion includes, among others, a widening primitive conversion of integral types (but not char), and that the reader has to refer to the definition of widening primitive conversion to understand the conversion of which type will be unconditionally exact and which ones are not. The difference here is that at first I thought the author re-defined a widening primitive conversion as a conversion from any integral type to any integral type. ?a widening primitive conversion which is? is the wording that caused my confusion. I?d like to confirm that my understanding is correct. Also, may it be a source of potential confusion for future readers of the spec? Thank you, Ella Ananeva -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Wed Oct 18 03:07:21 2023 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 17 Oct 2023 22:07:21 -0500 Subject: A question on JEP 455 specification In-Reply-To: References: Message-ID: Sounds like ?which? should be ?that? instead. ?Which? is used to incidentally add descriptive information without affecting the logic of a statement. ?That? is used to narrow the identity of something (and therefore exclude other possibilities), which (no pun intended) obviously does affect the logic. ?I have a car which is blue? ?I have the car that is blue? -Archie On Tue, Oct 17, 2023 at 9:05 PM Ella Ananeva wrote: > Hi team, > > Reading the spec for JEP 455, I was puzzled with the following part: > > > > *The following conversions are unconditionally exact:* > > - *?* > - *a widening primitive conversion which is: * > - *from any integral type to any integral type and neither of them > is **char* > - *?.* > - *?* > > At first (at the first 5 times, I confess) I read it as ?an > unconditionally exact conversion is, among others, a widening primitive > conversion, e.g. a conversion from any integral type to any integral type, > but not char.? > > I understand now that what was likely meant is that an unconditionally > exact conversion includes, among others, a widening primitive conversion of > integral types (but not char), and that the reader has to refer to the > definition of widening primitive conversion to understand the conversion of > which type will be unconditionally exact and which ones are not. > > The difference here is that at first I thought the author re-defined a > widening primitive conversion as a conversion from *any integral type to > any integral type*. ?a widening primitive conversion which is? is the > wording that caused my confusion. > > I?d like to confirm that my understanding is correct. Also, may it be a > source of potential confusion for future readers of the spec? > > Thank you, > > Ella Ananeva > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tanksherman27 at gmail.com Wed Oct 18 03:34:23 2023 From: tanksherman27 at gmail.com (Julian Waters) Date: Wed, 18 Oct 2023 11:34:23 +0800 Subject: Is there a way to extend unnamed classes to somehow facilitate top level static methods in Java? Message-ID: Hi all, Quick question: Java is often criticized for having too much emphasis on Object Oriented Programming, with every method having to be marked static repeatedly if one wants to opt out of OOP. Recently, unnamed classes and instance main methods were released into Java 21, which got me thinking: Can we somehow use them (unnamed classes) to facilitate top level "classless" methods in Java? Obviously, the methods would in actuality belong to the unnamed class and aren't actually classless, and internally would be static to avoid relying on object instances (since they're supposed to be top level). Whether or not we'd want to support top level global variables this way too I leave up to discussion. Or maybe there's a different way to have free form methods (such as with Kotlin) without having to mark entire utility classes as static somehow? Thoughts? best regards, Julian -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Oct 18 14:00:43 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 18 Oct 2023 10:00:43 -0400 Subject: A question on JEP 455 specification In-Reply-To: References: Message-ID: <1e7d883c-e6bc-560d-df63-7651008bbc01@oracle.com> Your interpretation is correct: the conversions are defined elsewhere, and this paragraph defines no new conversions, it just says "these are the ones that are unconditionally exact" (certain primitive widenings, boxing, etc.) On 10/17/2023 10:04 PM, Ella Ananeva wrote: > > Hi team, > > Reading the spec for JEP 455, I was puzzled with the following part: > > /The following conversions are unconditionally exact:/ > > * /?/ > * /a widening primitive conversion which is: / > o /from any integral type to any integral type and neither of > them is //char/// > o /?./ > * /?/ > > At first (at the first 5 times, I confess) I read it as ?an > unconditionally exact conversion is, among others, a widening > primitive conversion, e.g. a conversion from any integral type to any > integral type, but not char.? > > I understand now that what was likely meant is that an unconditionally > exact conversion includes, among others, a widening primitive > conversion of integral types (but not char), and that the reader has > to refer to the definition of widening primitive conversion to > understand the conversion of which type will be unconditionally exact > and which ones are not. > > The difference here is that at first I thought the author re-defined a > widening primitive conversion as a conversion from /any integral type > to any integral type/. ?a widening primitive conversion which is? is > the wording that caused my confusion. > > I?d like to confirm that my understanding is correct. Also, may it be > a source of potential confusion for future readers of the spec? > > Thank you, > > Ella Ananeva > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Oct 18 14:14:26 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 18 Oct 2023 10:14:26 -0400 Subject: Is there a way to extend unnamed classes to somehow facilitate top level static methods in Java? In-Reply-To: References: Message-ID: <9e0645e1-7d02-9c44-e4e7-a003d911a3f6@oracle.com> Let me separate your points a bit. Are unnamed classes a form of free functions in disguise? Emphatically no.? There are class declarations there, you just don't have to explicitly type them, and they are not really supposed to escape their own compilation unit (clever programmers can make this happen, but that's not the goal.) So let's rephrase your question to "I think free functions are cool, should Java have them?"? As you can probably imagine, this was discussed in the design of unnamed classes. The main question surrounding free functions is scoping; when a compilation unit declares a free function, what is its scope? One obvious answer is "the compilation unit", but in this case, the feature offers us relatively little, just a choice of where to declare it.? This might be convenient in a minor way, but would surely not carry its weight.? (Remember, no feature is "small", and every feature carries risks of foreclosing on future, better features.) The other choice is that free functions would be in scope in any class that imports the compilation unit declaring them (making them effectively global).? This offers more payback, but at a significant cost: namespace pollution, discoverability (how do I know what to import?), and readability (when I see a bare function name, how do I know how to interpret it?)? I don't think we'll be going down this route. On 10/17/2023 11:34 PM, Julian Waters wrote: > Hi all, > > Quick question: Java is often criticized for having too much emphasis > on Object Oriented Programming,?with every method having to be marked > static repeatedly if one wants to opt out of OOP. Recently, unnamed > classes and instance main methods were released into Java 21, which > got me thinking: Can we somehow use them (unnamed classes) to > facilitate top level "classless" methods in Java? Obviously, the > methods would in actuality belong to the unnamed class and aren't > actually classless,?and internally would be static to avoid relying on > object instances (since they're supposed to be top level). Whether or > not we'd want to support top level global variables this way too I > leave up to discussion. Or maybe there's a different way to have free > form methods (such as with Kotlin) without having to mark entire > utility classes as static somehow? > > Thoughts? > > best regards, > Julian -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Oct 19 00:01:42 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Wed, 18 Oct 2023 20:01:42 -0400 Subject: Reconstruction expression with explicit deconstuctor (REWED) In-Reply-To: References: Message-ID: Also adding @amber-dev On Wed, Oct 18, 2023 at 8:00?PM David Alayachew wrote: > Adding -comments > > On Sat, Sep 23, 2023 at 10:54?AM Attila Kelemen < > attila.kelemen85 at gmail.com> wrote: > >> Note: I think you would have a better chance, if you sent the email to >> "amber-spec-comments" than "observers", since the people who can actually >> make changes are more likely to read the "comments" list. >> >> That said, I don't really see the usefulness of such a syntax (and adding >> new syntax in itself is a detriment). That is, if I understood you >> correctly, then your example would replace this: >> >> ``` >> Point p = ...; >> Point pp = new Point(p.x() + 2, 0); >> ``` >> >> which is shorter and a lot more obvious. And if we were to go to more >> complicated things (many properties), then it doesn't help that much, >> because as far as I understand you, your proposal is still positional in >> its deconstruction pattern, and the positional nature is usually what is >> awkward in structures with many properties. >> >> >> Harrison Chikwe ezt ?rta (id?pont: 2023. szept. >> 23., Szo, 12:01): >> >>> Hi, >>> >>> I think that it is very useful to have when working with immutable >>> objects. >>> >>> For the purposes of this mail, let's call the design described in the >>> ambro-doc[1]: >>> reconstruction expression with implicit deconstructor (aka REWID). >>> >>> Has it been considered to make the deconstructor pattern explicit in the >>> reconstruction >>> expression (aka REWED)? >>> Doing so may remove the requirement to make names more significant in >>> the language. >>> >>> For example, given the following: >>> >>> Point p = ...; >>> Point pp = p with(int x, int y) { x += 2; y = 0; } >>> >>> We can interpret the reconstruction expression above as: >>> >>> . Find a deconstructor and constructor pair that has a signature that >>> matches (int, int) >>> . Deconstruct the target with the deconstructor >>> . Execute the block on RHS >>> . Invoke the constructor with the result. >>> >>> If we split the reconstruction expression into three steps: >>> >>> 1. Extraction >>> 2. Transformation >>> 3. Construction >>> >>> In REWID users are required only to do step 2 (transformation) and the >>> system handles 1 and 3. Whereas in REWED users are required to do step 1 >>> and 2 >>> and the system handles just step 3. >>> >>> # Other forms of REWED >>> >>> 1. Explicit Named deconstructor pattern >>> >>> Point pp = p with Point(int x, int y) {...} >>> >>> 2. Explicit Var deconstructor pattern >>> >>> Point pp = p with var(int x, int y) {...} >>> >>> 3. Explicit Unnamed deconstructor pattern >>> >>> Point pp = p with(int x, int y) {...} >>> >>> >>> We can extend form 1 to include factory pattern: >>> >>> Point pp = p with Point.factory(int x, int y) {...} >>> >>> >>> # Advantages >>> >>> 1. No need to make names significant. >>> 2. Can be combined with other patterns (e.g. unnamed pattern, var >>> pattern) >>> >>> Point pp = p with(int x, _) { x = 0; } >>> >>> >>> Nyehamene. >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From ella.ananeva at oracle.com Thu Oct 19 17:01:35 2023 From: ella.ananeva at oracle.com (Ella Ananeva) Date: Thu, 19 Oct 2023 17:01:35 +0000 Subject: Float to byte and back in switch Message-ID: Hi team, When we match labels in switch with primitive types, the spec defines the behavior for integral types and for floating-point types. ? If T is a primitive type, then a case label with a case constant c applies to a value that is of type char, byte, short, int, long, float, double, boolean, String or an enum type if the constant c is equal to the value. Equality is defined in terms of the == operator (15.21) for the integral types and the boolean type, and in terms of representation equivalence (java.lang.Double) for the floating-point types. If the value is a String, equality is defined in terms of the equals method of class String. What should be the behavior, though, when we try to match an integral type to a floating-point type? e.g., In this scenario: private static void testSwitch(float y) { System.out.println(y == 1); //prints true switch (y) { case 1 -> { System.out.println(y == 1);}//throws a compile-time error: constant label of type int is not compatible with switch selector type float default -> { } } } I would expect the compiler to be able to handle this situation: since int can be converted to float and the equality operator returns true, the label should be chosen. The opposite situation with narrowing conversion, when we try to convert float to int may be trickier, but shouldn?t it also be accepted? Thank you, Ella Ananeva -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Thu Oct 19 23:41:31 2023 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Thu, 19 Oct 2023 23:41:31 +0000 Subject: Float to byte and back in switch In-Reply-To: References: Message-ID: That section defines the runtime semantics of switch label selection indeed. What is also important is the kind of types that can be "allowed" to reach this point. The permitted case constant associations are shown in 14.11.1 Switch Blocks where we split (add) the new kinds of constants separately. if T is one of char, byte, short, int, Character, Byte, Short, Integer, or String, the constant is assignment compatible with T. if T is one of long, float, double, boolean, Long, Float, Double, Boolean, the constant is the same type: with T if T is a primitive type, or with T' where T' is T after unboxing conversion, otherwise. That second bullet essentially says that a selector of a float type can match only float constant labels and so on and so forth. While comparing y to 1 (the integer one) is crystal clear, there are other occasions that this is not. Consider this: jshell> float f = 16777216 // 224 and that can be represented exactly into a float f ==> 1.6777216E7 jshell> f == 16777217 // 224+1 that cannot be represented exactly because the bits of the mantissa do not suffice, so the RHS constant maps also to 1.6777216E7 $7 ==> true As result the following switch is not permitted: void test(float f) { switch (f) { case 16777216: break; case 16777217: break; default: break; } } both labels map to the same float while the two labels are clearly, representationally different (among them). That would lead to great confusion. ________________________________ From: amber-dev on behalf of Ella Ananeva Sent: 19 October 2023 19:01 To: amber-dev at openjdk.org Subject: Float to byte and back in switch Hi team, When we match labels in switch with primitive types, the spec defines the behavior for integral types and for floating-point types. ? If T is a primitive type, then a case label with a case constant c applies to a value that is of type char, byte, short, int, long, float, double, boolean, String or an enum type if the constant c is equal to the value. Equality is defined in terms of the == operator (15.21) for the integral types and the boolean type, and in terms of representation equivalence (java.lang.Double) for the floating-point types. If the value is a String, equality is defined in terms of the equals method of class String. What should be the behavior, though, when we try to match an integral type to a floating-point type? e.g., In this scenario: private static void testSwitch(float y) { System.out.println(y == 1); //prints true switch (y) { case 1 -> { System.out.println(y == 1);}//throws a compile-time error: constant label of type int is not compatible with switch selector type float default -> { } } } I would expect the compiler to be able to handle this situation: since int can be converted to float and the equality operator returns true, the label should be chosen. The opposite situation with narrowing conversion, when we try to convert float to int may be trickier, but shouldn?t it also be accepted? Thank you, Ella Ananeva -------------- next part -------------- An HTML attachment was scrubbed... URL: From ella.ananeva at oracle.com Thu Oct 19 23:47:39 2023 From: ella.ananeva at oracle.com (Ella Ananeva) Date: Thu, 19 Oct 2023 23:47:39 +0000 Subject: Float to byte and back in switch In-Reply-To: References: Message-ID: Thanks for clarifying, Angelos! That helps. From: Angelos Bimpoudis Date: Thursday, October 19, 2023 at 4:41 PM To: Ella Ananeva , amber-dev at openjdk.org Subject: Re: Float to byte and back in switch That section defines the runtime semantics of switch label selection indeed. What is also important is the kind of types that can be "allowed" to reach this point. The permitted case constant associations are shown in 14.11.1 Switch Blocks where we split (add) the new kinds of constants separately. if T is one of char, byte, short, int, Character, Byte, Short, Integer, or String, the constant is assignment compatible with T. if T is one of long, float, double, boolean, Long, Float, Double, Boolean, the constant is the same type: with T if T is a primitive type, or with T' where T' is T after unboxing conversion, otherwise. That second bullet essentially says that a selector of a float type can match only float constant labels and so on and so forth. While comparing y to 1 (the integer one) is crystal clear, there are other occasions that this is not. Consider this: jshell> float f = 16777216 // 224 and that can be represented exactly into a float f ==> 1.6777216E7 jshell> f == 16777217 // 224+1 that cannot be represented exactly because the bits of the mantissa do not suffice, so the RHS constant maps also to 1.6777216E7 $7 ==> true As result the following switch is not permitted: void test(float f) { switch (f) { case 16777216: break; case 16777217: break; default: break; } } both labels map to the same float while the two labels are clearly, representationally different (among them). That would lead to great confusion. ________________________________ From: amber-dev on behalf of Ella Ananeva Sent: 19 October 2023 19:01 To: amber-dev at openjdk.org Subject: Float to byte and back in switch Hi team, When we match labels in switch with primitive types, the spec defines the behavior for integral types and for floating-point types. ? If T is a primitive type, then a case label with a case constant c applies to a value that is of type char, byte, short, int, long, float, double, boolean, String or an enum type if the constant c is equal to the value. Equality is defined in terms of the == operator (15.21) for the integral types and the boolean type, and in terms of representation equivalence (java.lang.Double) for the floating-point types. If the value is a String, equality is defined in terms of the equals method of class String. What should be the behavior, though, when we try to match an integral type to a floating-point type? e.g., In this scenario: private static void testSwitch(float y) { System.out.println(y == 1); //prints true switch (y) { case 1 -> { System.out.println(y == 1);}//throws a compile-time error: constant label of type int is not compatible with switch selector type float default -> { } } } I would expect the compiler to be able to handle this situation: since int can be converted to float and the equality operator returns true, the label should be chosen. The opposite situation with narrowing conversion, when we try to convert float to int may be trickier, but shouldn?t it also be accepted? Thank you, Ella Ananeva -------------- next part -------------- An HTML attachment was scrubbed... URL: From lukes at google.com Fri Oct 20 20:10:46 2023 From: lukes at google.com (Luke Sandberg) Date: Fri, 20 Oct 2023 13:10:46 -0700 Subject: Condy for LambdaMetafactory? Message-ID: Hi, as part of a custom compiler I am working on I stumbled across https://bugs.openjdk.org/browse/JDK-8198418 which references invoking LambdaMetafactory.metafactory from a condy instruction when there are no bound parameters. I see that there is support for this in BootstrapMethodInvoker: https://github.com/openjdk/jdk/blob/77b2394c46bd304ffc0658cb758d971f1f2940b6/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java#L136C28-L136C55 But there is no method implementing this signature that i can find. Is this a half implemented idea from the original condy implementation? Or am I missing something? Initially i thought that BootstrapMethodInvoker might handle the signature delta, but experimentally that is not true :D Thanks, -Luke -------------- next part -------------- An HTML attachment was scrubbed... URL: From pato at dudits.net Fri Oct 20 12:09:43 2023 From: pato at dudits.net (=?UTF-8?Q?Patrik_Dudit=C5=A1?=) Date: Fri, 20 Oct 2023 14:09:43 +0200 Subject: StringTemplate - why it is an unsealed interface? Message-ID: Hello, I've been experimenting with StringTemplates recently for a few weeks. One thing that appeared odd to me is that StringTemplate is defined as an unsealed interface, as if it would make sense for users of the API to create their own implementations of it. I would like to ask whether that is an omission, or there is some other underlying reason for that. Patrik Dudit? -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Oct 20 20:13:58 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 20 Oct 2023 16:13:58 -0400 Subject: Condy for LambdaMetafactory? In-Reply-To: References: Message-ID: There was an experiment to translate non-capturing lambdas with condy, which was never integrated.? As you observed, it requires changes to the bootstrap method signature; when we started adding the alternate bootstrap method, we realized there were some cleanups in the LMF spec/implementation we wanted to do, but these ended up getting back-burnered. On 10/20/2023 4:10 PM, Luke Sandberg wrote: > Hi, as part of a custom compiler I am working on I stumbled across > https://bugs.openjdk.org/browse/JDK-8198418 which references invoking > LambdaMetafactory.metafactory from a condy instruction when there are > no bound parameters. > > I see that there is support for this in BootstrapMethodInvoker: > https://github.com/openjdk/jdk/blob/77b2394c46bd304ffc0658cb758d971f1f2940b6/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java#L136C28-L136C55 > > But there is no method implementing this signature that i can find. > > Is this a half implemented idea from the original condy > implementation? Or am I missing something?? Initially?i thought that > BootstrapMethodInvoker might handle the signature delta, but > experimentally that is not true :D > > Thanks, > -Luke -------------- next part -------------- An HTML attachment was scrubbed... URL: From lukes at google.com Fri Oct 20 20:29:22 2023 From: lukes at google.com (Luke Sandberg) Date: Fri, 20 Oct 2023 13:29:22 -0700 Subject: Condy for LambdaMetafactory? In-Reply-To: References: Message-ID: Classic story of development scope creep :P, thanks for confirming. Experimentally, I can trigger the special case in BootstrapMethodInvoker by just writing my own shim bootstrap method public static Object constantMetafactory( MethodHandles.Lookup lookup, String name, Class type, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType) throws Throwable { return LambdaMetafactory.metafactory( lookup, name, methodType(type), samMethodType, implMethod, instantiatedMethodType) .getTarget() .invoke(); } This allows me to retarget a number of invokedynamic callsites to constant dynamic and saves a small amount of classfile bytes. Do you think this is a valuable optimization? I'm guessing it would only make a difference in terms of bootstrapping.. perhaps condy is a simpler thing for the vm to manage than an indy+constantcallsite? Or perhaps I should just assume that since the vm engineers abandoned working on this, it wasn't particularly valuable? Thanks again! -Luke On Fri, Oct 20, 2023 at 1:14?PM Brian Goetz wrote: > There was an experiment to translate non-capturing lambdas with condy, > which was never integrated. As you observed, it requires changes to the > bootstrap method signature; when we started adding the alternate bootstrap > method, we realized there were some cleanups in the LMF spec/implementation > we wanted to do, but these ended up getting back-burnered. > > On 10/20/2023 4:10 PM, Luke Sandberg wrote: > > Hi, as part of a custom compiler I am working on I stumbled across > https://bugs.openjdk.org/browse/JDK-8198418 which references invoking > LambdaMetafactory.metafactory from a condy instruction when there are no > bound parameters. > > I see that there is support for this in BootstrapMethodInvoker: > https://github.com/openjdk/jdk/blob/77b2394c46bd304ffc0658cb758d971f1f2940b6/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java#L136C28-L136C55 > > But there is no method implementing this signature that i can find. > > Is this a half implemented idea from the original condy implementation? Or > am I missing something? Initially i thought that BootstrapMethodInvoker > might handle the signature delta, but experimentally that is not true :D > > Thanks, > -Luke > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Oct 20 20:59:27 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 20 Oct 2023 16:59:27 -0400 Subject: Condy for LambdaMetafactory? In-Reply-To: References: Message-ID: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> The optimization is appealing because condy linkage is cheaper than indy linkage, and because it would allow more sharing between identical lambdas in the same file (they'd map to the same condy, which would only have to be resolved once.) The main blocker was "hmm, LMF has some cornery spec issues, before we add yet another bootstrap variant, let's look into those".? I suppose we might consider timing out on that particular investigation and proceeding, though, if we had evidence that the startup was improved. On 10/20/2023 4:29 PM, Luke Sandberg wrote: > Classic story of development scope creep :P, thanks for confirming. > > Experimentally, I can trigger the special case in > BootstrapMethodInvoker by just writing my own shim bootstrap method > > ? public static Object constantMetafactory( > ? ? ? MethodHandles.Lookup lookup, > ? ? ? String name, > ? ? ? Class type, > ? ? ? MethodType samMethodType, > ? ? ? MethodHandle implMethod, > ? ? ? MethodType instantiatedMethodType) > ? ? ? throws Throwable { > ? ? return LambdaMetafactory.metafactory( > ? ? ? ? ? ? lookup, name, methodType(type), samMethodType, implMethod, > instantiatedMethodType) > ? ? ? ? .getTarget() > ? ? ? ? .invoke(); > ? } > > This allows me to retarget a number of invokedynamic callsites to > constant dynamic and saves a small amount?of classfile bytes. > > Do you think this is a valuable optimization?? I'm guessing it would > only make a difference in terms of bootstrapping.. perhaps condy is a > simpler thing for the vm to manage than an indy+constantcallsite?? Or > perhaps I should just assume that since the vm engineers abandoned > working on this, it wasn't particularly valuable? > > Thanks again! > -Luke > > > > On Fri, Oct 20, 2023 at 1:14?PM Brian Goetz > wrote: > > There was an experiment to translate non-capturing lambdas with > condy, which was never integrated.? As you observed, it requires > changes to the bootstrap method signature; when we started adding > the alternate bootstrap method, we realized there were some > cleanups in the LMF spec/implementation we wanted to do, but these > ended up getting back-burnered. > > On 10/20/2023 4:10 PM, Luke Sandberg wrote: >> Hi, as part of a custom compiler I am working on I stumbled >> across https://bugs.openjdk.org/browse/JDK-8198418 which >> references invoking LambdaMetafactory.metafactory from a condy >> instruction when there are no bound parameters. >> >> I see that there is support for this in BootstrapMethodInvoker: >> https://github.com/openjdk/jdk/blob/77b2394c46bd304ffc0658cb758d971f1f2940b6/src/java.base/share/classes/java/lang/invoke/BootstrapMethodInvoker.java#L136C28-L136C55 >> >> >> But there is no method implementing this signature that i can find. >> >> Is this a half implemented idea from the original condy >> implementation? Or am I missing something? Initially?i thought >> that BootstrapMethodInvoker might handle the signature delta, but >> experimentally that is not true :D >> >> Thanks, >> -Luke > -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Fri Oct 20 21:25:19 2023 From: james.laskey at oracle.com (Jim Laskey) Date: Fri, 20 Oct 2023 21:25:19 +0000 Subject: StringTemplate - why it is an unsealed interface? In-Reply-To: References: Message-ID: <73F878B1-1B7E-4A7E-96B5-7FFD43B8E051@oracle.com> Yes, it was left intentionally unsealed to allow for other implementations. I may be odd-thinking, but my thought was that other languages or DSLs might want their template objects to interact with Java template processors. That is, assuming that 3rd party template processors will become ubiquitous. An implementation might implement a parser that would parse a file and result in a specialized StringTemplate, maybe parsing on the fly so the whole file doesn?t need to be read in. Note the parser could also use any style embedded expression it wanted. Ex: Department of Agriculture ?DATE? ?FIRSTNAME? ?INITIAL?. ?SURNAME? ?ADDRESS? ?CITY?, ?STATE?, ?COUNTRY? ?POSTAL? Dear ?FIRSTNAME?, I wish to inform you that your application for a sunflower seed permit has been approved. You should receive said permit within three weeks. Do not hesitate to enquire on any issue. Sincerely, Joan Smith Director The embedded expressions here might be the names of fields in a DB and the target processor retrieves the actual value based on the embedded expression. Loosely something like: while (resultSet.hasNext()) { ... StringTemplate.Processor dbFill = st -> { List fragments = st.fragments() List values = st.values().stream() .map(Object::toString) .map(v -> resultSet.getString(v)) .toList(); return StringTemplate.interpolate(fragments, values); }; String result = dbFill.process(temple); ... } Since the processing here is somewhat generic, someone else could apply the same processor using templates generated by Kotlin. Bottom line is to create an ecosystem that has a lot of template processors with the potential for a variety of template sources. Cheers, ? Jim On Oct 20, 2023, at 9:09?AM, Patrik Dudit? wrote: Hello, I've been experimenting with StringTemplates recently for a few weeks. One thing that appeared odd to me is that StringTemplate is defined as an unsealed interface, as if it would make sense for users of the API to create their own implementations of it. I would like to ask whether that is an omission, or there is some other underlying reason for that. Patrik Dudit? -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Sat Oct 21 18:59:14 2023 From: john.r.rose at oracle.com (John Rose) Date: Sat, 21 Oct 2023 11:59:14 -0700 Subject: Condy for LambdaMetafactory? In-Reply-To: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> References: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> Message-ID: <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> On 20 Oct 2023, at 13:59, Brian Goetz wrote: > The optimization is appealing because condy linkage is cheaper than indy linkage, and because it would allow more sharing between identical lambdas in the same file (they'd map to the same condy, which would only have to be resolved once.) Using unshared lambdas has a small advantage which is sometimes significant. Lambdas accumulate type profiles, and those type profiles add an extra hidden layer of customization to the lambdas, exploited by the JIT sometimes. So you might have two identical lambdas, for which the JIT produces completely different code, because it devirtualizes something in the lambda (after inlining the desugared body), and devirtualizes differently for the two different lambdas. All I?m saying here is that sharing is not always a win. Sometimes the opposite move, splitting and customization, is the performance win, even though you have two copies of the code in the end. So, Luke is proposing an interesting experiment, one which is likely to improve startup and footprint, but could also leave some technical debt to the profiler and JIT. Note also that JIT code quality impacts startup and warmup; you can see startup itself get worse if you do something the JIT doesn?t like. The fun thing about invokedynamic (unlike any other instruction) is that it relinks itself for every distinct occurrence of the instruction. (Other instructions become fully linked as soon as their CP entries link, so sharing or splitting the instructions does not affect the resolution of CP entries.) If you have two identical invokedynamic instructions, they incur two resolutions. This means the LMF might possibly spin two identical but distinct lambda classes. That might seem to be a waste, but there are hidden benefits. The invokedynamic instruction turns the default towards ?split them all? while the condy constant turns it towards ?share them all?. From tzengshinfu at gmail.com Sun Oct 22 15:52:40 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Sun, 22 Oct 2023 23:52:40 +0800 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: Hi, folks: When I switched my primary programming language from C# to Java, I found myself perplexed by 'string comparison' (and still do at times). While string comparisons can sometimes become quite intricate, involving issues like case sensitivity, cultural nuances... most of the time, all that's needed is string1 == string2. I discovered that a similar question was asked a decade ago ( https://www.reddit.com/r/java/comments/1gjwpu/will_the_equals_operator_ever_be_fixed_with/), with responses indicating that it's due to 'Backward compatibility,' and therefore, unlikely to change. (Backward compatibility! We just keep piling new things on top of historical baggage, and for users coming from school or from other languages like C#, Python, C++, Rust, Golang, Kotlin, Scala, JavaScript, PHP, Rlang, Swift, Ruby, Dart... the top 20 languages according to PYPL, having to consult the so-called 'Java FAQ' can be frustrating. But I believe that if something is amiss, it should be corrected to keep moving forward. It would be fantastic if this issue could be addressed in a new version of Java and an automatic conversion feature provided to fix places in user code that use String.equals. (Similar to the JVM's preview feature switch) Is the Class-File API a potential solution to this problem? Is my idea unrealistic? /* GET BETTER EVERY DAY */ -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Sun Oct 22 18:01:06 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Sun, 22 Oct 2023 18:01:06 +0000 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: > On 22 Oct 2023, at 16:52, tzengshinfu wrote: > > Backward compatibility! We just keep piling new things on top of historical baggage While I can?t comment on future changes in this specific area, I have to point out that backward compatibility isn?t just a nice thing to have, but one of the biggest and most important selling points for a language, and one of the main reasons Java has succeeded in supporting large and long-lasting software to a greater degree than all of the languages you mentioned with the possible exception of C++ (where string equality is also not so trivial) and C (which you don?t mention but where string comparison is also quirky). Backward compatibility is not entirely binary and there are levels to breaking it ? and sometimes we do. But the bar to breaking something substantial needs to be very high ? you need to show that a change that breaks something significant has a truly extraordinary positive impact ? because good backward compatibility is why Java is being chosen more than any other language today to write large, important, ?serious? software. While an individual developer may not appreciate it, a CTO who has to pick a language for an important piece of software that is meant to last for a couple of decades cares a lot about not needing to do unnecessary work due to changes in the language. The last thing you want to do is so casually dismiss the great importance of that feature. ? Ron From davidalayachew at gmail.com Sun Oct 22 19:29:58 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 22 Oct 2023 15:29:58 -0400 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: Hello, Thank you for reaching out! I'm pretty sure that the amber-dev mailing list is not the correct place for this type of question. This topic usually goes on at the following mailing list instead. I've CC'd it for you. I would also encourage you to remove amber-dev from your CC when responding to me, or anyone else on this thread. discuss at openjdk.org To answer your question, this is a very common request, and the biggest answer is definitely still the backwards compatibility problem. But tbh, the question I have for you is this -- is it such a big cost to call the o1.equals(o2) method instead of using ==? And if you want to handle nulls too, you can import java.util.Objects (that class is full of useful static utility methods) and then just say Objects.equals(o1, o2) instead. I am pretty sure that that exact method was created in response to your exact question. I understand it might be inconvenient, but making a change like you suggested would be very disruptive for very little benefit. All you would gain from doing this would be a slightly better syntax for representing object equality and a little more ease when it comes to teaching somebody Java. Is that really worth the effort? As for the class-file api, I'll CC them so that someone can fact check me. Assuming I'm not wrong (no one responds to that point specifically), I would also drop that mailing list from your CC when responding. The purpose of the Class-File API was to build and transform class files. So that seems unrelated to what you want. You want to repurpose old syntax, but syntax stops being relevant after compilation, and it is these compiled class files that the Class-File API deals in. If we tried to use that API to handle class files created with the old syntax, then we would have a migration and clarity problem, amongst much more. Let us know if you have any more questions. Thank you for your time! David Alayachew On Sun, Oct 22, 2023 at 2:12?PM tzengshinfu wrote: > Hi, folks: > > When I switched my primary programming language from C# to Java, I found > myself perplexed by 'string comparison' (and still do at times). While > string comparisons can sometimes become quite intricate, involving issues > like case sensitivity, cultural nuances... most of the time, all that's > needed is string1 == string2. > > I discovered that a similar question was asked a decade ago ( > https://www.reddit.com/r/java/comments/1gjwpu/will_the_equals_operator_ever_be_fixed_with/), > with responses indicating that it's due to 'Backward compatibility,' and > therefore, unlikely to change. (Backward compatibility! We just keep piling > new things on top of historical baggage, and for users coming from school > or from other languages like C#, Python, C++, Rust, Golang, Kotlin, Scala, > JavaScript, PHP, Rlang, Swift, Ruby, Dart... the top 20 languages according > to PYPL, having to consult the so-called 'Java FAQ' can be frustrating. > > But I believe that if something is amiss, it should be corrected to keep > moving forward. It would be fantastic if this issue could be addressed in a > new version of Java and an automatic conversion feature provided to fix > places in user code that use String.equals. (Similar to the JVM's preview > feature switch) Is the Class-File API a potential solution to this problem? > Is my idea unrealistic? > > /* GET BETTER EVERY DAY */ > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Oct 23 04:35:00 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 23 Oct 2023 00:35:00 -0400 Subject: Silly and likely non-important question about StringTemplates. Message-ID: Hello Amber Dev Team, I've been playing with StringTemplates a bit more, and I had a quick question. I repeatedly find myself in the same situation -- I have a String that is already in a variable, and I want it to be the full String in the StringTemplate. What is the recommended way to do it? For example. String abc = "xyz"; MY_PROCESSOR. <--- what goes here? Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 23 05:06:51 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 23 Oct 2023 07:06:51 +0200 (CEST) Subject: Silly and likely non-important question about StringTemplates. In-Reply-To: References: Message-ID: <45915114.32113571.1698037611682.JavaMail.zimbra@univ-eiffel.fr> > From: "David Alayachew" > To: "amber-dev" > Sent: Monday, October 23, 2023 6:35:00 AM > Subject: Silly and likely non-important question about StringTemplates. > Hello Amber Dev Team, > I've been playing with StringTemplates a bit more, and I had a quick question. > I repeatedly find myself in the same situation -- I have a String that is > already in a variable, and I want it to be the full String in the > StringTemplate. What is the recommended way to do it? > For example. > String abc = "xyz"; > MY_PROCESSOR. <--- what goes here? MY_PROCESSOR."\{abc}" > Thank you for your time! > David Alayachew regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Oct 23 10:21:18 2023 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 23 Oct 2023 11:21:18 +0100 Subject: Condy for LambdaMetafactory? In-Reply-To: <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> References: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> Message-ID: <5ad697e1-2bac-4ddd-be98-c03ed3e95be2@oracle.com> On 21/10/2023 19:59, John Rose wrote: > All I?m saying here is that sharing is not always a win. Sometimes the > opposite move, splitting and customization, is the performance win, > even though you have two copies of the code in the end. Something related to this topic, if we revisit this: currently javac deduplicates lambda bodies that are identical [1], so that only one BSM entry is generated (and then reused). The logic for doing that is a bit finicky, and it seems to go against the principle you outline above John. So, as we shift towards constant lambdas, perhaps there's a chance to look at this again. Cheers Maurizio [1] - https://bugs.openjdk.org/browse/JDK-8200301 From davidalayachew at gmail.com Mon Oct 23 13:01:32 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 23 Oct 2023 09:01:32 -0400 Subject: Silly and likely non-important question about StringTemplates. In-Reply-To: <45915114.32113571.1698037611682.JavaMail.zimbra@univ-eiffel.fr> References: <45915114.32113571.1698037611682.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hello R?mi, Thank you for your response! I am making a SWING_HTML String Template Processor that turns basic multi-line strings into ones that are enclosed, and replace new lines with
. So, for me, most of my Strings are already made, and thus, don't need to have anything "inserted" into them. private static final Processor SWING_HTML = st -> "" + st .interpolate() .lines() .collect(java.util.stream.Collectors.joining("
")) + "" ; It would be nice if Java put this particular processor into the Swing library itself at some point. But it's easy enough for me to implement myself, which is a nice thing about String Templates. Plus, this feature composes -- I can make an italics or bold processor that can translate to the various html tags that Swing supports. And it's safer than just putting in the tags myself because the curly braces will turn into compiler errors if I don't align them correctly. Plus, curly braces are a syntax feature, so most ide's (even VIM and EMACS!) will give you hints about where your curly braces are, making it much easier to trace issues with this. Thank you for your time and help! David Alayachew On Mon, Oct 23, 2023 at 1:06?AM Remi Forax wrote: > > > ------------------------------ > > *From: *"David Alayachew" > *To: *"amber-dev" > *Sent: *Monday, October 23, 2023 6:35:00 AM > *Subject: *Silly and likely non-important question about StringTemplates. > > Hello Amber Dev Team, > > I've been playing with StringTemplates a bit more, and I had a quick > question. > > I repeatedly find myself in the same situation -- I have a String that is > already in a variable, and I want it to be the full String in the > StringTemplate. What is the recommended way to do it? > > For example. > > String abc = "xyz"; > MY_PROCESSOR. <--- what goes here? > > > MY_PROCESSOR."\{abc}" > > > Thank you for your time! > David Alayachew > > > regards, > R?mi > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 23 13:05:51 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 23 Oct 2023 15:05:51 +0200 (CEST) Subject: Silly and likely non-important question about StringTemplates. In-Reply-To: References: <45915114.32113571.1698037611682.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <1734597655.32698423.1698066351500.JavaMail.zimbra@univ-eiffel.fr> > From: "David Alayachew" > To: "Remi Forax" > Cc: "amber-dev" > Sent: Monday, October 23, 2023 3:01:32 PM > Subject: Re: Silly and likely non-important question about StringTemplates. > Hello R?mi, > Thank you for your response! > I am making a SWING_HTML String Template Processor that turns basic multi-line > strings into ones that are enclosed, and replace new lines with
. > So, for me, most of my Strings are already made, and thus, don't need to have > anything "inserted" into them. > private static final Processor SWING_HTML = > st -> > "" > + > st > .interpolate() > .lines() > .collect(java.util.stream.Collectors.joining("
")) > + > "" > ; > It would be nice if Java put this particular processor into the Swing library > itself at some point. But it's easy enough for me to implement myself, which is > a nice thing about String Templates. Plus, this feature composes -- I can make > an italics or bold processor that can translate to the various html tags that > Swing supports. And it's safer than just putting in the tags myself because the > curly braces will turn into compiler errors if I don't align them correctly. > Plus, curly braces are a syntax feature, so most ide's (even VIM and EMACS!) > will give you hints about where your curly braces are, making it much easier to > trace issues with this. Hello, don't forget to escape <, > et & correctly. > Thank you for your time and help! > David Alayachew regards, R?mi > On Mon, Oct 23, 2023 at 1:06 AM Remi Forax < [ mailto:forax at univ-mlv.fr | > forax at univ-mlv.fr ] > wrote: >>> From: "David Alayachew" < [ mailto:davidalayachew at gmail.com | >>> davidalayachew at gmail.com ] > >>> To: "amber-dev" < [ mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] > >>> Sent: Monday, October 23, 2023 6:35:00 AM >>> Subject: Silly and likely non-important question about StringTemplates. >>> Hello Amber Dev Team, >>> I've been playing with StringTemplates a bit more, and I had a quick question. >>> I repeatedly find myself in the same situation -- I have a String that is >>> already in a variable, and I want it to be the full String in the >>> StringTemplate. What is the recommended way to do it? >>> For example. >>> String abc = "xyz"; >>> MY_PROCESSOR. <--- what goes here? >> MY_PROCESSOR."\{abc}" >>> Thank you for your time! >>> David Alayachew >> regards, >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From lukes at google.com Sun Oct 22 16:20:10 2023 From: lukes at google.com (Luke Sandberg) Date: Sun, 22 Oct 2023 09:20:10 -0700 Subject: Condy for LambdaMetafactory? In-Reply-To: <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> References: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> Message-ID: In my case this happens to not be a concern for structural reasons... The lambdas in question are used to satisfy one of the 3 available calling conventions for functions in my languages (we can compile calls to 'static positional calls', aka, invokestatic on a function like R foo(V v1,V v2,V v3,...), 'static map calls' aka invokestatic on R foo(Map params), or we can capture functions as values and pass them around, in which case we capture the second signature in a lambda. For each function we just compile 3 static methods with the 3 signatures public static CompiledTemplate foo() { return ThisClass::foo; } <-- this is the lambda capture I am considering replacing with condy public static Result foo(Map m,...) { return foo(m.get("v1"), m.get("v2"),...); } public static Result foo(V v1, V v2,...) {...} So all callers calling the lambda method share an instance if it changes to use condy or doesn't. The method dispatching within the lambda is also itself an `invokestatic` style call, so I don't think there even is a penalty? Since the type profiling opportunities are statically analyzable (is the capture a virtual method? is there a signature mismatch between the interface and the delegate?), does this imply that there are some simple rules that the compiler could use to split the difference? or perhaps LMF itself could opportunistically cache and reuse lambda instances when we expect that the jit has no particular opportunities? Or maybe this is all fraught since putting logic in the compiler or runtime that assumes particular behavior of the JIT is just a risky proposition. This does remind me of an odd issue I ran into with condy where I actually wanted to split two condys that were identical to be executed twice to preserve identity semantics. I ended up solving this by just adding dummy parameters to my bootstrap methods. A similar solution could be used for LMF+condy where we preserve 'distinctly' spun classes but gain 'faster linkage' On Sat, Oct 21, 2023 at 11:59?AM John Rose wrote: > On 20 Oct 2023, at 13:59, Brian Goetz wrote: > > > The optimization is appealing because condy linkage is cheaper than indy > linkage, and because it would allow more sharing between identical lambdas > in the same file (they'd map to the same condy, which would only have to be > resolved once.) > > Using unshared lambdas has a small advantage which is sometimes > significant. > Lambdas accumulate type profiles, and those type profiles add an extra > hidden layer of customization to the lambdas, exploited by the JIT > sometimes. > > So you might have two identical lambdas, for which the JIT produces > completely > different code, because it devirtualizes something in the lambda (after > inlining the desugared body), and devirtualizes differently for the two > different lambdas. > > All I?m saying here is that sharing is not always a win. Sometimes the > opposite move, splitting and customization, is the performance win, > even though you have two copies of the code in the end. > > So, Luke is proposing an interesting experiment, one which is likely to > improve startup and footprint, but could also leave some technical debt > to the profiler and JIT. Note also that JIT code quality impacts > startup and warmup; you can see startup itself get worse if you do > something the JIT doesn?t like. > > The fun thing about invokedynamic (unlike any other instruction) is that > it relinks itself for every distinct occurrence of the instruction. > (Other instructions become fully linked as soon as their CP entries > link, so sharing or splitting the instructions does not affect the > resolution of CP entries.) If you have two identical invokedynamic > instructions, they incur two resolutions. This means the LMF might > possibly spin two identical but distinct lambda classes. That might > seem to be a waste, but there are hidden benefits. The invokedynamic > instruction turns the default towards ?split them all? while the > condy constant turns it towards ?share them all?. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Oct 23 13:53:16 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 23 Oct 2023 09:53:16 -0400 Subject: Silly and likely non-important question about StringTemplates. In-Reply-To: <1734597655.32698423.1698066351500.JavaMail.zimbra@univ-eiffel.fr> References: <45915114.32113571.1698037611682.JavaMail.zimbra@univ-eiffel.fr> <1734597655.32698423.1698066351500.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Ah yes, thank you very much. The beauty of this is that, if I had caught that error later, only one place in my code needs to change. Everything would flow through that. A shame that I can't signify this with some type, so that I know that the String has already been SWING_HTML'ified (in the vein of Lexi Lambda's "Parse, don't validate" ). I could wrap it in a wrapper, but I can't put the wrapper into my JLabel. On Mon, Oct 23, 2023 at 9:08?AM wrote: > > > ------------------------------ > > *From: *"David Alayachew" > *To: *"Remi Forax" > *Cc: *"amber-dev" > *Sent: *Monday, October 23, 2023 3:01:32 PM > *Subject: *Re: Silly and likely non-important question about > StringTemplates. > > Hello R?mi, > > Thank you for your response! > > I am making a SWING_HTML String Template Processor that turns basic > multi-line strings into ones that are enclosed, and replace new > lines with
. So, for me, most of my Strings are already made, and thus, > don't need to have anything "inserted" into them. > > private static final Processor SWING_HTML = > st -> > "" > + > st > .interpolate() > .lines() > .collect(java.util.stream.Collectors.joining("
")) > + > "" > ; > > It would be nice if Java put this particular processor into the Swing > library itself at some point. But it's easy enough for me to implement > myself, which is a nice thing about String Templates. Plus, this feature > composes -- I can make an italics or bold processor that can translate to > the various html tags that Swing supports. And it's safer than just putting > in the tags myself because the curly braces will turn into compiler errors > if I don't align them correctly. Plus, curly braces are a syntax feature, > so most ide's (even VIM and EMACS!) will give you hints about where your > curly braces are, making it much easier to trace issues with this. > > > Hello, > don't forget to escape <, > et & correctly. > > > Thank you for your time and help! > David Alayachew > > > regards, > R?mi > > > > On Mon, Oct 23, 2023 at 1:06?AM Remi Forax wrote: > >> >> >> ------------------------------ >> >> *From: *"David Alayachew" >> *To: *"amber-dev" >> *Sent: *Monday, October 23, 2023 6:35:00 AM >> *Subject: *Silly and likely non-important question about StringTemplates. >> >> Hello Amber Dev Team, >> >> I've been playing with StringTemplates a bit more, and I had a quick >> question. >> >> I repeatedly find myself in the same situation -- I have a String that is >> already in a variable, and I want it to be the full String in the >> StringTemplate. What is the recommended way to do it? >> >> For example. >> >> String abc = "xyz"; >> MY_PROCESSOR. <--- what goes here? >> >> >> MY_PROCESSOR."\{abc}" >> >> >> Thank you for your time! >> David Alayachew >> >> >> regards, >> R?mi >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Oct 23 14:42:17 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 23 Oct 2023 10:42:17 -0400 Subject: A couple questions about String Templates Message-ID: Hello Amber Dev Team, I have recently started playing with String Templates a lot more. They are an excellent feature that makes handling Strings safer and easier than before. I am pleasantly surprised. As the number of my String Templates grows (currently 6 on a single project), I am starting to think about how different String Template Processors work together. Currently, I am working on building a SWING_HTML String Template Processor, which wraps the text into tags, replaces new lines with
, and does escaping. However, I also want to be able to do Swing HTML things, like bolding, italicizing, and more. More specifically, I want to elevate this functionality into the type system. My initial instinct is to delegate the task to other processors. For example, SWING_HTML."Hello \{BOLD."\{name}"}!" However, one thing that I want to be able to do is to be able to ensure that, if I do (for example) delegate bolding to another processor, then that certain processor is only ever used with my SWING_HTML processor. As in, a BOLD template processor does not make much sense if it is being used outside of my SWING_HTML template processor. I am not particularly attached to the delegating (don't want this to become an XY problem). But the primary goal of being a one stop shop for all things Swing HTML -- is that something I can do with String Templates, where the individual components like bolding and italicizing can be lifted into the Java syntax? Or is that asking too much/not worth the effort -- I should just stick to writing tags manually/interpreting certain character sequences as bold or italicized? In the same vein, I have another question. I am wondering if I am subtly drifting out of bounds of the goals of a String Template. The syntax is attractive, so I find myself using it often. However, I am starting to think that I am not using it for what it was meant for. For example, in my SWING_HTML processor, I find myself just passing in arguments like so. SWING_HTML."\{stringText}" It's technically valid, but is this the sort of thing that should just be a method instead? Is it more of a subtle border/gradient that we should decide as a matter of trade offs? Yet another question that builds off of the second one. Oftentimes, we receive a String that we would like to break apart into parts so that we can put it back together safely. Breaking it apart into parts is not the responsibility of String Template Processor, but putting it back together safely 100% is. Since String Templates are now (or very soon to be) the de facto way of putting Strings together safely/properly, what would be the the way of breaking them apart safely/properly? This question about parallelism in strategies is born out of seeing how you all are handling pattern-matching. Object construction and deconstruction are parallels of each other. It sort of leads me to wonder if there is a similar sort of play happening here for String deconstruction. My mind wants to say java.util.regex.Pattern/Matcher, but I want to confirm that. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Oct 23 14:53:13 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 23 Oct 2023 10:53:13 -0400 Subject: A couple questions about String Templates In-Reply-To: References: Message-ID: Some more thoughts about delegating processors. The image that just started bumping around in my head was that of inner classes. If I wanted to solve this type of problem with normal Java, the first thing I would think of would be to do it with inner classes. It would not be the iron clad guard rails I am looking for, but it would be a clear, visual signifier that any bolding I am doing should be done with respect to the SWING_HTML. The reason why that matters to me is because, as the number of my template processors increases, I start to create little "groups" of template processors that handle separate things. For example, I have processors in regards to things like italicizing or bolding. Then I have ones in regards to highlighting, which further delegates to color. But that is where the warning bells went off -- how do I clearly differentiate between foreground color and background color? Yes, obviously, I could just name them foreground and background, but the point remains -- is the strategy to use naming to differentiate it? Or is there some way to essentially compose processors where I can dissuade, if not outright prevent, misuse of 2 similar but separate concepts? I really want to be able to lean into the type system in order to get this, but I am not sure if that means I should just forgo String Templates and jump straight into a method and do it the "long way". On Mon, Oct 23, 2023 at 10:42?AM David Alayachew wrote: > Hello Amber Dev Team, > > I have recently started playing with String Templates a lot more. They are > an excellent feature that makes handling Strings safer and easier than > before. I am pleasantly surprised. > > As the number of my String Templates grows (currently 6 on a single > project), I am starting to think about how different String Template > Processors work together. > > Currently, I am working on building a SWING_HTML String Template > Processor, which wraps the text into tags, replaces new lines with >
, and does escaping. However, I also want to be able to do Swing HTML > things, like bolding, italicizing, and more. More specifically, I want to > elevate this functionality into the type system. My initial instinct is to > delegate the task to other processors. For example, SWING_HTML."Hello > \{BOLD."\{name}"}!" > > However, one thing that I want to be able to do is to be able to ensure > that, if I do (for example) delegate bolding to another processor, then > that certain processor is only ever used with my SWING_HTML processor. As > in, a BOLD template processor does not make much sense if it is being used > outside of my SWING_HTML template processor. > > I am not particularly attached to the delegating (don't want this to > become an XY problem). But the primary goal of being a one stop shop for > all things Swing HTML -- is that something I can do with String Templates, > where the individual components like bolding and italicizing can be lifted > into the Java syntax? Or is that asking too much/not worth the effort -- I > should just stick to writing tags manually/interpreting certain character > sequences as bold or italicized? > > In the same vein, I have another question. > > I am wondering if I am subtly drifting out of bounds of the goals of a > String Template. The syntax is attractive, so I find myself using it often. > However, I am starting to think that I am not using it for what it was > meant for. > > For example, in my SWING_HTML processor, I find myself just passing in > arguments like so. > > SWING_HTML."\{stringText}" > > It's technically valid, but is this the sort of thing that should just be > a method instead? Is it more of a subtle border/gradient that we should > decide as a matter of trade offs? > > Yet another question that builds off of the second one. > > Oftentimes, we receive a String that we would like to break apart into > parts so that we can put it back together safely. Breaking it apart into > parts is not the responsibility of String Template Processor, but putting > it back together safely 100% is. Since String Templates are now (or very > soon to be) the de facto way of putting Strings together safely/properly, > what would be the the way of breaking them apart safely/properly? > > This question about parallelism in strategies is born out of seeing how > you all are handling pattern-matching. Object construction and > deconstruction are parallels of each other. It sort of leads me to wonder > if there is a similar sort of play happening here for String > deconstruction. My mind wants to say java.util.regex.Pattern/Matcher, but I > want to confirm that. > > Thank you for your time and help! > David Alayachew > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mark.reinhold at oracle.com Mon Oct 23 15:49:47 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Mon, 23 Oct 2023 15:49:47 +0000 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: <20231023114945.393941906@eggemoggin.niobe.net> 2023/10/22 15:29:58 -0400, David Alayachew : > Hello, > > Thank you for reaching out! > > I'm pretty sure that the amber-dev mailing list is not the correct place > for this type of question. This topic usually goes on at the following > mailing list instead. I've CC'd it for you. I would also encourage you to > remove amber-dev from your CC when responding to me, or anyone else on this > thread. > > discuss at openjdk.org In fact, the discuss list is not appropriate for this type of question. Per the list?s description [1]: This list is for general discussion about the OpenJDK Community, including proposals of new Projects and Groups. This is not a place to discuss specific technical proposals. For that, please use an appropriate area-specific mailing list. This is not a place to ask support questions. Please direct those to whoever provided your JDK build. The OpenJDK Developers? Guide includes a list of area-specific mailing lists [2], but there isn?t a list specifically for the Java programming language. Given that the charter of Project Amber [3] is to ?explore and incubate smaller, productivity-oriented Java language features,? the original poster?s choice of amber-dev was reasonable. - Mark [1] https://mail.openjdk.org/mailman/listinfo/discuss [2] https://openjdk.org/guide/#area-mailing-lists [3] https://openjdk.org/projects/amber/ From davidalayachew at gmail.com Mon Oct 23 21:03:26 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 23 Oct 2023 17:03:26 -0400 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: <20231023114945.393941906@eggemoggin.niobe.net> References: <20231023114945.393941906@eggemoggin.niobe.net> Message-ID: Ok. Thanks for the heads up. On Mon, Oct 23, 2023 at 11:49?AM Mark Reinhold wrote: > 2023/10/22 15:29:58 -0400, David Alayachew : > > Hello, > > > > Thank you for reaching out! > > > > I'm pretty sure that the amber-dev mailing list is not the correct place > > for this type of question. This topic usually goes on at the following > > mailing list instead. I've CC'd it for you. I would also encourage you to > > remove amber-dev from your CC when responding to me, or anyone else on > this > > thread. > > > > discuss at openjdk.org > > In fact, the discuss list is not appropriate for this type of question. > Per the list?s description [1]: > > This list is for general discussion about the OpenJDK Community, > including proposals of new Projects and Groups. > > This is not a place to discuss specific technical proposals. For that, > please use an appropriate area-specific mailing list. > > This is not a place to ask support questions. Please direct those to > whoever provided your JDK build. > > The OpenJDK Developers? Guide includes a list of area-specific mailing > lists [2], but there isn?t a list specifically for the Java programming > language. Given that the charter of Project Amber [3] is to ?explore > and incubate smaller, productivity-oriented Java language features,? > the original poster?s choice of amber-dev was reasonable. > > - Mark > > > [1] https://mail.openjdk.org/mailman/listinfo/discuss > [2] https://openjdk.org/guide/#area-mailing-lists > [3] https://openjdk.org/projects/amber/ -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Oct 24 10:22:25 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 24 Oct 2023 12:22:25 +0200 Subject: Condy for LambdaMetafactory? In-Reply-To: <5ad697e1-2bac-4ddd-be98-c03ed3e95be2@oracle.com> References: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> <5ad697e1-2bac-4ddd-be98-c03ed3e95be2@oracle.com> Message-ID: Oh, that's an interesting feature! Too bad, it works only when debugging info is off, which is probably less than 1% of compiled Java code I've seen. I've created a small sample to amuse people: import java.util.function.IntSupplier; import java.util.stream.IntStream; public class Benchmark { private int compute() { IntSupplier s1 = () -> IntStream.range(0, 10000).map(v -> 1).sum(); IntSupplier s2 = () -> IntStream.range(0, 10000).map(v -> 1).sum(); IntSupplier s3 = () -> IntStream.range(0, 10000).map(v -> 1).sum(); return s1.getAsInt() + s2.getAsInt() + s3.getAsInt(); } private void measure() { int res = 0; // Warmup for (int i = 0; i < 20000; i++) { res += compute(); } // Measurement long start = System.currentTimeMillis(); for (int i = 0; i < 20000; i++) { res += compute(); } long end = System.currentTimeMillis(); System.out.println(res); System.out.println("Duration: " + (end - start) + "ms"); } public static void main(String[] args) { new Benchmark().measure(); } } With -g the measured time is about 1800ms, while without -g it's about 6ms. That's because when lambdas are deduplicated, type profile for map() method is clean, and the whole stream can be inlined, and even the loop could be likely replaced with a constant. When lambdas are not deduplicated, this is not the case: type profile is polluted, and the whole stream cannot be inlined. So, deduplication can actually improve the performance, at least in such extreme cases. By the way, while experimenting with this sample, I've noticed that deduplication of this particular code is broken since Java 18 (the code performs slowly both with and without -g, and reading the bytecode confirms that no deduplication is done). Should I report, or probably there's no interest in supporting this feature? With best regards, Tagir Valeev On Mon, Oct 23, 2023 at 1:40?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 21/10/2023 19:59, John Rose wrote: > > All I?m saying here is that sharing is not always a win. Sometimes the > > opposite move, splitting and customization, is the performance win, > > even though you have two copies of the code in the end. > > Something related to this topic, if we revisit this: currently javac > deduplicates lambda bodies that are identical [1], so that only one BSM > entry is generated (and then reused). > > The logic for doing that is a bit finicky, and it seems to go against > the principle you outline above John. So, as we shift towards constant > lambdas, perhaps there's a chance to look at this again. > > Cheers > Maurizio > > [1] - https://bugs.openjdk.org/browse/JDK-8200301 > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Oct 24 10:41:25 2023 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 24 Oct 2023 11:41:25 +0100 Subject: Condy for LambdaMetafactory? In-Reply-To: References: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> <5ad697e1-2bac-4ddd-be98-c03ed3e95be2@oracle.com> Message-ID: On 24/10/2023 11:22, Tagir Valeev wrote: > With -g the measured time is about 1800ms, while without -g it's about > 6ms. That's because when lambdas are deduplicated, type profile for > map() method is clean, and the whole stream can be inlined, and even > the loop could be likely replaced with a constant. When lambdas are > not deduplicated, this is not the case: type profile is polluted, and > the whole stream cannot be inlined. So, deduplication can actually > improve the performance, at least in such extreme cases. Very interesting results - thanks! I believe it works well because when deduplication kicks in, the body is _really_ identical, so there's no value in keeping separate profiling info for the three lambdas. Well, maybe, maybe not. If your lambda has some conditional code (`if (cond) ...`) and one lambda is always used where cond = true and the other lambda is always used where cond = false, this might be a problem as the profile of the deduplicate lambda might get polluted. So I'm not sure how reliably this technique would actually work out in practice as a way to improve performance. > By the way, while experimenting with this sample, I've noticed that > deduplication of this particular code is broken since Java 18 (the > code performs slowly both with and without -g, and reading the > bytecode confirms that no deduplication is done). Should I report, or > probably there's no interest in supporting this feature? Oh - I wasn't aware of this issue - regardless of what we wanna do with this feature moving forward, filing a bug is always the right thing to do, to make sure it doesn't fall off the radar. Thanks Maurizio -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Oct 24 11:23:19 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 24 Oct 2023 13:23:19 +0200 Subject: Condy for LambdaMetafactory? In-Reply-To: References: <90b20166-783b-fa2c-1035-eb804a060452@oracle.com> <88EDFE69-51F2-4128-BEE0-4623C540402D@oracle.com> <5ad697e1-2bac-4ddd-be98-c03ed3e95be2@oracle.com> Message-ID: Done, thanks: https://bugs.openjdk.org/browse/JDK-8318712 On Tue, Oct 24, 2023 at 12:41?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 24/10/2023 11:22, Tagir Valeev wrote: > > With -g the measured time is about 1800ms, while without -g it's about 6ms. That's because when lambdas are deduplicated, type profile for map() method is clean, and the whole stream can be inlined, and even the loop could be likely replaced with a constant. When lambdas are not deduplicated, this is not the case: type profile is polluted, and the whole stream cannot be inlined. So, deduplication can actually improve the performance, at least in such extreme cases. > > Very interesting results - thanks! I believe it works well because when > deduplication kicks in, the body is _really_ identical, so there's no value > in keeping separate profiling info for the three lambdas. Well, maybe, > maybe not. If your lambda has some conditional code (`if (cond) ...`) and > one lambda is always used where cond = true and the other lambda is always > used where cond = false, this might be a problem as the profile of the > deduplicate lambda might get polluted. So I'm not sure how reliably this > technique would actually work out in practice as a way to improve > performance. > > By the way, while experimenting with this sample, I've noticed that deduplication of this particular code is broken since Java 18 (the code performs slowly both with and without -g, and reading the bytecode confirms that no deduplication is done). Should I report, or probably there's no interest in supporting this feature? > > Oh - I wasn't aware of this issue - regardless of what we wanna do with > this feature moving forward, filing a bug is always the right thing to do, > to make sure it doesn't fall off the radar. > > Thanks > Maurizio > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tzengshinfu at gmail.com Thu Oct 26 08:19:57 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Thu, 26 Oct 2023 16:19:57 +0800 Subject: Is there a possibility of the string equality operator (==) being fixed? Message-ID: Hello, folks: I've noticed that `string comparison` in Java can be confusing because, in certain contexts, their results are actually the same as `the wrapper classes of Primitive Data Types`, as shown below: ```java // wrapper class of int // PS:The constructor Integer(int) is deprecated since version 9 Integer int1 = new Integer(1); Integer int2 = new Integer(1); out.println(int1 == int2); // false out.println(int1.equals(int2)); // true String string1 = new String("1"); String string2 = new String("1"); out.println(string1 == string2); // false out.println(string1.equals(string2)); // true ``` After modifying the initialization and performing `+` operations, the Integer results match expectations, but the String results are unexpected: ```java Integer int1 = 1; Integer int2 = 1; Integer int3 = int1 + 1; Integer int4 = int2 + 1; out.println(int3 == int4); // true out.println(int3.equals(int4)); // true String string1 = "1"; String string2 = "1"; String string3 = string1 + "1"; String string4 = string2 + "1"; out.println(string3 == string4); // Expected result is `true`, but the actual result is `false`. out.println(string3.equals(string4)); // true ``` But it's not actually a mistake. Based on the naming convention, String is indeed a class, not a Primitive Data Type. Because it's a class, using `Object::equals()` to compare its contents is perfectly normal. However, from a user's perspective, as one of the commonly used functionalities, the logic for comparing Strings is different from other Primitive Data Types. This invisibly increases the learning curve for students, newcomers, and developers transitioning from other programming languages. It should be more user-friendly, especially for the aforementioned members. If there were a way to unify the comparison of strings with the comparison of other Primitive Data Types, it would help newcomers use it correctly and enter the Java world more rapidly. It would also lead to cleaner code and reduced typing for all developers. I believe there should be a way to improve this inconsistency. Personally, ever since I learned about `String::equals()`, I stopped using `==`, which led me to propose two solutions. Solution A: Make `String1 == String2` have the same result as `String1.equals(String2)`. I find Solution A to be simpler, but making abrupt changes might cause certain systems or software to break. How many disruptions would changing the result of `String1 == String2` cause? If it's rarely used or never used, we could safely remove it, but we need to measure the cost of this disruption. The challenge is how to measure it. For all the APIs distributed in the JDK and third-party packages, there are common APIs, but there are also less commonly used ones. We, as individual developers, can't know this data. We can only voice our opinions on mailing lists, Reddit, or GitHub and feel the response either upward or downward. Is there something like "telemetry"? Could we possibly collect statistics on various package/class/method/syntax during compilation and return those statistics after compilation? Or could we send surveys to Java developers and authors of major packages/libraries? The fear that "I think it's rarely used, but in reality, it's not, and everything breaks" holds people back. Additionally, another reason for hesitation is that there isn't a feature currently in place that scans and safely converts old projects to use updated syntax/APIs during JDK upgrades. So, what about Solution B? Solution B: Introduce a new class named `string/str` and deprecate the String class. We could create a new class, named `string` or `str` (following the convention of starting with a lowercase letter for Primitive Data Types). This class's `string comparison` behavior would be the same as Primitive Data Types, which means the result would be the same as `String::equals()`. Since it mimics a Primitive Data Type class, we would remove `string/str::equals()`. Then, we could mark the existing String class as "deprecated" and instruct users to use `string/str` instead. Unfortunately, `string/str` is not a reserved word, and we don't even know where it might have been used as a variable name or how many times. Fortunately, we have a precedent "JEP 443: Unnamed Patterns and Variables (Preview)" that used a lengthy preview period and compile-time warnings to encourage users to abandon reserved words ( _ ). I believe the same approach could work for `string/str`. I personally prefer Solution B because the safest approach would be to prioritize creating the 'new' to replace the 'old' and then, after a period, confirm that the usage of the 'old' has fallen below a certain threshold before removing it. However, as Solution A mentioned, the current situation is that I don't have good data to support either modifying or removing the 'old' action. Similarly, I don't have good data to support whether creating the 'new' is necessary. I agree that "backward compatibility" is a cornerstone of Java's, and any language's, economic system's growth. Thanks to this persistence, we can confidently open projects from many years ago in front of our bosses without worrying about them breaking. Golang has also maintained its ecosystem thanks to "backward compatibility", although it retains the right to break it under certain circumstances ( https://go.dev/doc/go1compat). However, because Java has a long history, to remain competitive, it will make various changes to its specifications. In my opinion, "backward compatibility" at such times should not mean maintaining an unchanged status quo but giving developers ample time to adapt to changes, at least when change is possible. Changing the syntax of `string comparison` would, I believe, make Java more user-friendly, change the perception of Java as a language with a lot of ancient baggage, and reduce the cognitive burden on developers. But do others share this belief, or is it just me? Finally, thanks to David's reminder, Brian mentioned, "there's a possibility in the future that we can use .equals() for state comparison on any Primitive Data Types (since everything can be treated as an Object). My perspective is to propose a hypothesis: since String/string and other Primitive Data Types are commonly used, can we simulate them to behave like a single Primitive Data Type in order to coordinate their behavior? Another reason is that I personally find that a == b is clearer than a.equals(b) and less prone to typos (although it's not a problem with the assistance of modern IDEs). From a visual perspective, Andrew also brought up an interesting point, using String1 .= String2 as syntactic sugar for .equals() is much clearer (or maybe we can use String1 === String2?). /* GET BETTER EVERY DAY */ -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Oct 26 14:18:45 2023 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 26 Oct 2023 09:18:45 -0500 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: On Thu, Oct 26, 2023 at 5:21?AM tzengshinfu wrote: > I've noticed that `string comparison` in Java can be confusing ... > This idea strikes me as misguided. What you're saying does make sense - IF you are looking at Java from the perspective of another programming language where strings have a special, unique type defined in the language itself. For example, Javascript, which has "boolean", "number", "string", "object", etc. But for this discussion, try to break out of that mindset. What you're missing is that Java was designed based on different design principles. Let's go back in history for a moment... The design of Java was in part an anti-complexity reaction to the overly complex design of C++. And Java became wildly successful as a result. This is totally analogous to how Unix was an anti-complexity reaction to the overly complex design of Multics. And Unix became wildly successful as a result. In Java, you have 8 primitive types and Object. That's it! Everything that can contain state is one of those types. If you can understand 9 things, you can understand everything. In Unix all system resources are expressed as files, including block devices, serial ports, etc. If you know how to open and read a file, you know how to work with all of those system resources. One and done! In Javascript, if you want a string, you use the "string" type of the language. In Java, you use the class "java.lang.String" which is a normal class just like any other. In Javascript, if you want a map, you use the "object" type of the language. In Java, you use "java.util.HashMap", or "java.util.TreeMap", or whatever. Again, these are normal classes just like any other. So to change how String behaves would be to introduce a 10th "thing" into the language... No thanks!! -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Oct 26 15:52:39 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 26 Oct 2023 11:52:39 -0400 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: Part of what you are saying is that "if we had a time machine, we should have reconsidered whether `String` should have been a primitive in the language." And I agree, String is pretty special, that might have been a worthwhile conversation to have (in fact, I'm sure pretty it was had.)? But you're leaping from there to "of course it should have been done that way", and then from there to "the only reason we are not fixing it now is a misguided, slavish adherence to compatibility."? Both of these leaps are wrong. Let me give you an idea of the cost of what you are suggesting. Your "solution" B want to create a new type, `string`, that is better than the old String.? Let's take for sake of argument that it really will be better.? That's the benefit.? But let's look at the costs: ?- Every single Java API ever written uses String as an argument type, a return type, a field type, a type parameter, etc.? So under this plan, now 100% of the Java code out there instantly becomes "the old code", and either needs to migrate, or clients will stumble over converting between `String` and `string`. This is a tax that will literally hit almost every line of Java code ever written. ?- Even if a migration like this could be pulled off, how long do you think it will take to get to a world where we don't have both "old string" and "new string" simultaneously?? Now users will have to learn *both ways* and keep track of their subtle differences. Having seen many, many proposals for improving the language, I can say with confidence that the most dangerous source of such proposals is the desire to "fix" "mistakes".? The cost of "fixing" those "mistakes" often exceeds the benefit by orders of magnitude. "Solution" A is problematic in a different way.? You are arguing that String is so special that it alone should have different == semantics than every other class.? Now, I agree that String is pretty special, but such "solutions" have a cost too; they add ad-hoc, arbitrary complexity to the language, which every user has to learn.? Now users have to learn `==` means one thing for primitives, another for object references, but a third weird new thing for strings.? Which further makes it more likely that they will misuse `==` on other object references, because that's how String works!? (And do you believe for a minute that if we did this to String, there wouldn't be calls to do the same for, say, LocalDateTime?)? While the situation we have is not perfect, it at least has simple, stable, principled, easy-to-understand rules.? Trading that for complex, ad-hoc, ever-changing, hard-to-keep-track-of rules would require a benefit many orders of magnitude bigger. On 10/26/2023 4:19 AM, tzengshinfu wrote: > Hello, folks: > > I've noticed that `string comparison` in Java can be confusing > because, in certain contexts, > their results are actually the same as `the wrapper classes of > Primitive Data Types`, as shown below: > ```java > ? ? // wrapper class of int > ? ? // PS:The constructor Integer(int) is deprecated since version 9 > ? ? Integer int1 = new Integer(1); > ? ? Integer int2 = new Integer(1); > ? ? out.println(int1 == int2); // false > ? ? out.println(int1.equals(int2)); // true > > ? ? String string1 = new String("1"); > ? ? String string2 = new String("1"); > ? ? out.println(string1 == string2); // false > ? ? out.println(string1.equals(string2)); // true > ``` > > After modifying the initialization and performing `+` operations, > the Integer results match expectations, but the String results are > unexpected: > ```java > ? ? Integer int1 = 1; > ? ? Integer int2 = 1; > ? ? Integer int3 = int1 + 1; > ? ? Integer int4 = int2 + 1; > ? ? out.println(int3 == int4); // true > ? ? out.println(int3.equals(int4)); // true > > ? ? String string1 = "1"; > ? ? String string2 = "1"; > ? ? String string3 = string1 + "1"; > ? ? String string4 = string2 + "1"; > ? ? out.println(string3 == string4); // Expected result is `true`, but > the actual result is `false`. > ? ? out.println(string3.equals(string4)); // true > ``` > > But it's not actually a mistake. > Based on the naming convention, String is indeed a class, not a > Primitive Data Type. > Because it's a class, using `Object::equals()` to compare its contents > is perfectly normal. > > However, from a user's perspective, as one of the commonly used > functionalities, > the logic for comparing Strings is different from other Primitive Data > Types. > This invisibly increases the learning curve for students, newcomers, > and developers transitioning from other programming languages. > It should be more user-friendly, especially for the aforementioned > members. > > If there were a way to unify the comparison of strings with the > comparison of other Primitive Data Types, > it would help newcomers use it correctly and enter the Java world more > rapidly. > It would also lead to cleaner code and reduced typing for all developers. > > I believe there should be a way to improve this inconsistency. > Personally, ever since I learned about `String::equals()`, I stopped > using `==`, > which led me to propose two solutions. > > Solution A: Make `String1 == String2` have the same result as > `String1.equals(String2)`. > > I find Solution A to be simpler, but making abrupt changes might cause > certain systems or software to break. > How many disruptions would changing the result of `String1 == String2` > cause? > If it's rarely used or never used, we could safely remove it, but we > need to measure the cost of this disruption. > The challenge is how to measure it. > > For all the APIs distributed in the JDK and third-party packages, > there are common APIs, > but there are also less commonly used ones. We, as individual > developers, can't know this data. > We can only voice our opinions on mailing lists, Reddit, or GitHub and > feel the response either upward or downward. > Is there something like "telemetry"? > Could we possibly collect statistics on various > package/class/method/syntax during compilation and return those > statistics after compilation? > Or could we send surveys to Java developers and authors of major > packages/libraries? > > The fear that "I think it's rarely used, but in reality, it's not, and > everything breaks" holds people back. > Additionally, another reason for hesitation is that there isn't a > feature currently in place that scans and safely converts old projects > to use updated syntax/APIs during JDK upgrades. > > So, what about Solution B? > > Solution B: Introduce a new class named `string/str` and deprecate the > String class. > > We could create a new class, named `string` or `str` (following the > convention of starting with a lowercase letter for Primitive Data Types). > This class's `string comparison` behavior would be the same as > Primitive Data Types, which means the result would be the same as > `String::equals()`. > Since it mimics a Primitive Data Type class, we would remove > `string/str::equals()`. > > Then, we could mark the existing String class as "deprecated" and > instruct users to use `string/str` instead. > > Unfortunately, `string/str` is not a reserved word, and we don't even > know where it might have been used as a variable name or how many times. > Fortunately, we have a precedent "JEP 443: Unnamed Patterns and > Variables (Preview)" that used a lengthy preview period and > compile-time warnings to encourage users to abandon reserved words ( _ ). > I believe the same approach could work for `string/str`. > > I personally prefer Solution B because the safest approach would be to > prioritize creating the 'new' to replace the 'old' and then, after a > period, > confirm that the usage of the 'old' has fallen below a certain > threshold before removing it. > > However, as Solution A mentioned, the current situation is that I > don't have good data to support either modifying or removing the 'old' > action. > Similarly, I don't have good data to support whether creating the > 'new' is necessary. > > I agree that "backward compatibility" is a cornerstone of Java's, and > any language's, economic system's growth. > Thanks to this persistence, we can confidently open projects from many > years ago in front of our bosses without worrying about them breaking. > Golang has also maintained its ecosystem thanks to "backward > compatibility", > although it retains the right to break it under certain circumstances > (https://go.dev/doc/go1compat). > > However, because Java has a long history, to remain competitive, it > will make various changes to its specifications. > In my opinion, "backward compatibility" at such times should not mean > maintaining an unchanged status quo but giving developers ample time > to adapt to changes, at least when change is possible. > > Changing the syntax of `string comparison` would, I believe, make Java > more user-friendly, change the perception of Java as a language with a > lot of ancient baggage, > and reduce the cognitive burden on developers. > But do others share this belief, or is it just me? > > Finally, thanks to David's reminder, Brian mentioned, "there's a > possibility in the future that we can use .equals() for state > comparison on any Primitive Data Types (since everything can be > treated as an Object). My perspective is to propose a hypothesis: > since String/string and other Primitive Data Types are commonly used, > can we simulate them to behave like a single Primitive Data Type in > order to coordinate their behavior? Another reason is that I > personally find that a == b is clearer than a.equals(b) and less prone > to typos (although it's not a problem with the assistance of modern > IDEs). From a visual perspective, Andrew also brought up an > interesting point, using String1 .= String2 as syntactic sugar for > .equals() is much clearer (or maybe we can use String1 === String2?). > > > /* GET BETTER EVERY DAY */ -------------- next part -------------- An HTML attachment was scrubbed... URL: From numeralnathan at gmail.com Thu Oct 26 19:06:51 2023 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Thu, 26 Oct 2023 12:06:51 -0700 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: Making == work like equals() for String has concurrent algorithm implications. Many times when writing a concurrent algorithm, I need identity == and not equals(). So, if == becomes equals() for String, then I would need a new operator that works like ==. We would then have to introduce a new operator and we are back to having something that works like == but isn't ==. In other words, fixing == means adding a new operator and Java becomes very convoluted. On Thu, Oct 26, 2023, 11:27 AM Brian Goetz wrote: > Part of what you are saying is that "if we had a time machine, we should > have reconsidered whether `String` should have been a primitive in the > language." And I agree, String is pretty special, that might have been a > worthwhile conversation to have (in fact, I'm sure pretty it was had.) But > you're leaping from there to "of course it should have been done that way", > and then from there to "the only reason we are not fixing it now is a > misguided, slavish adherence to compatibility." Both of these leaps are > wrong. > > Let me give you an idea of the cost of what you are suggesting. Your > "solution" B want to create a new type, `string`, that is better than the > old String. Let's take for sake of argument that it really will be > better. That's the benefit. But let's look at the costs: > > - Every single Java API ever written uses String as an argument type, a > return type, a field type, a type parameter, etc. So under this plan, now > 100% of the Java code out there instantly becomes "the old code", and > either needs to migrate, or clients will stumble over converting between > `String` and `string`. This is a tax that will literally hit almost every > line of Java code ever written. > > - Even if a migration like this could be pulled off, how long do you > think it will take to get to a world where we don't have both "old string" > and "new string" simultaneously? Now users will have to learn *both ways* > and keep track of their subtle differences. > > Having seen many, many proposals for improving the language, I can say > with confidence that the most dangerous source of such proposals is the > desire to "fix" "mistakes". The cost of "fixing" those "mistakes" often > exceeds the benefit by orders of magnitude. > > "Solution" A is problematic in a different way. You are arguing that > String is so special that it alone should have different == semantics than > every other class. Now, I agree that String is pretty special, but such > "solutions" have a cost too; they add ad-hoc, arbitrary complexity to the > language, which every user has to learn. Now users have to learn `==` > means one thing for primitives, another for object references, but a third > weird new thing for strings. Which further makes it more likely that they > will misuse `==` on other object references, because that's how String > works! (And do you believe for a minute that if we did this to String, > there wouldn't be calls to do the same for, say, LocalDateTime?) While the > situation we have is not perfect, it at least has simple, stable, > principled, easy-to-understand rules. Trading that for complex, ad-hoc, > ever-changing, hard-to-keep-track-of rules would require a benefit many > orders of magnitude bigger. > > > > > On 10/26/2023 4:19 AM, tzengshinfu wrote: > > Hello, folks: > > I've noticed that `string comparison` in Java can be confusing because, in > certain contexts, > their results are actually the same as `the wrapper classes of Primitive > Data Types`, as shown below: > ```java > // wrapper class of int > // PS:The constructor Integer(int) is deprecated since version 9 > Integer int1 = new Integer(1); > Integer int2 = new Integer(1); > out.println(int1 == int2); // false > out.println(int1.equals(int2)); // true > > String string1 = new String("1"); > String string2 = new String("1"); > out.println(string1 == string2); // false > out.println(string1.equals(string2)); // true > ``` > > After modifying the initialization and performing `+` operations, > the Integer results match expectations, but the String results are > unexpected: > ```java > Integer int1 = 1; > Integer int2 = 1; > Integer int3 = int1 + 1; > Integer int4 = int2 + 1; > out.println(int3 == int4); // true > out.println(int3.equals(int4)); // true > > String string1 = "1"; > String string2 = "1"; > String string3 = string1 + "1"; > String string4 = string2 + "1"; > out.println(string3 == string4); // Expected result is `true`, but the > actual result is `false`. > out.println(string3.equals(string4)); // true > ``` > > But it's not actually a mistake. > Based on the naming convention, String is indeed a class, not a Primitive > Data Type. > Because it's a class, using `Object::equals()` to compare its contents is > perfectly normal. > > However, from a user's perspective, as one of the commonly used > functionalities, > the logic for comparing Strings is different from other Primitive Data > Types. > This invisibly increases the learning curve for students, newcomers, > and developers transitioning from other programming languages. > It should be more user-friendly, especially for the aforementioned members. > > If there were a way to unify the comparison of strings with the comparison > of other Primitive Data Types, > it would help newcomers use it correctly and enter the Java world more > rapidly. > It would also lead to cleaner code and reduced typing for all developers. > > I believe there should be a way to improve this inconsistency. > Personally, ever since I learned about `String::equals()`, I stopped using > `==`, > which led me to propose two solutions. > > Solution A: Make `String1 == String2` have the same result as > `String1.equals(String2)`. > > I find Solution A to be simpler, but making abrupt changes might cause > certain systems or software to break. > How many disruptions would changing the result of `String1 == String2` > cause? > If it's rarely used or never used, we could safely remove it, but we need > to measure the cost of this disruption. > The challenge is how to measure it. > > For all the APIs distributed in the JDK and third-party packages, there > are common APIs, > but there are also less commonly used ones. We, as individual developers, > can't know this data. > We can only voice our opinions on mailing lists, Reddit, or GitHub and > feel the response either upward or downward. > Is there something like "telemetry"? > Could we possibly collect statistics on various > package/class/method/syntax during compilation and return those statistics > after compilation? > Or could we send surveys to Java developers and authors of major > packages/libraries? > > The fear that "I think it's rarely used, but in reality, it's not, and > everything breaks" holds people back. > Additionally, another reason for hesitation is that there isn't a feature > currently in place that scans and safely converts old projects to use > updated syntax/APIs during JDK upgrades. > > So, what about Solution B? > > Solution B: Introduce a new class named `string/str` and deprecate the > String class. > > We could create a new class, named `string` or `str` (following the > convention of starting with a lowercase letter for Primitive Data Types). > This class's `string comparison` behavior would be the same as Primitive > Data Types, which means the result would be the same as `String::equals()`. > Since it mimics a Primitive Data Type class, we would remove > `string/str::equals()`. > > Then, we could mark the existing String class as "deprecated" and instruct > users to use `string/str` instead. > > Unfortunately, `string/str` is not a reserved word, and we don't even know > where it might have been used as a variable name or how many times. > Fortunately, we have a precedent "JEP 443: Unnamed Patterns and Variables > (Preview)" that used a lengthy preview period and compile-time warnings to > encourage users to abandon reserved words ( _ ). > I believe the same approach could work for `string/str`. > > I personally prefer Solution B because the safest approach would be to > prioritize creating the 'new' to replace the 'old' and then, after a period, > confirm that the usage of the 'old' has fallen below a certain threshold > before removing it. > > However, as Solution A mentioned, the current situation is that I don't > have good data to support either modifying or removing the 'old' action. > Similarly, I don't have good data to support whether creating the 'new' is > necessary. > > I agree that "backward compatibility" is a cornerstone of Java's, and any > language's, economic system's growth. > Thanks to this persistence, we can confidently open projects from many > years ago in front of our bosses without worrying about them breaking. > Golang has also maintained its ecosystem thanks to "backward > compatibility", > although it retains the right to break it under certain circumstances ( > https://go.dev/doc/go1compat). > > However, because Java has a long history, to remain competitive, it will > make various changes to its specifications. > In my opinion, "backward compatibility" at such times should not mean > maintaining an unchanged status quo but giving developers ample time to > adapt to changes, at least when change is possible. > > Changing the syntax of `string comparison` would, I believe, make Java > more user-friendly, change the perception of Java as a language with a lot > of ancient baggage, > and reduce the cognitive burden on developers. > But do others share this belief, or is it just me? > > Finally, thanks to David's reminder, Brian mentioned, "there's a > possibility in the future that we can use .equals() for state comparison on > any Primitive Data Types (since everything can be treated as an Object). My > perspective is to propose a hypothesis: since String/string and other > Primitive Data Types are commonly used, can we simulate them to behave like > a single Primitive Data Type in order to coordinate their behavior? Another > reason is that I personally find that a == b is clearer than a.equals(b) > and less prone to typos (although it's not a problem with the assistance of > modern IDEs). From a visual perspective, Andrew also brought up an > interesting point, using String1 .= String2 as syntactic sugar for > .equals() is much clearer (or maybe we can use String1 === String2?). > > > /* GET BETTER EVERY DAY */ > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From johannes.spangenberg at hotmail.de Sun Oct 29 01:16:16 2023 From: johannes.spangenberg at hotmail.de (Johannes Spangenberg) Date: Sun, 29 Oct 2023 02:16:16 +0100 Subject: Type parameters and other topics regarding String Templates Message-ID: Hello, I recently thought about String Templates and just read the new JEP 459 for the second preview. I have four comments about the current JEP, more or less ordered by importance. *1) Unusual generic types for exceptions and return values* The generic types as specified by JEP 459 look like remnants to me. Since template expressions are syntactic sugar for method calls, we should just use the type of the process method, as was introduced by this JEP. However, the generic types were somehow left in the JEP. Is there a good reason for that? For example, java.lang.AutoCloseable does not have a generic type for the exception. The only advantage I see is that you can implement processors using Lambdas without introducing a functional interface. How often do you have a processor that is trivial enough that you see the benefit of using a lambda? Even the implementation of STR takes multiple lines. If somebody needs a functional interface, it is trivial to create one yourself. In my opinion, the noise introduced by the template parameters greatly overshadows the advantage of lambdas without having to declare a functional interface first. For RAW, STR, and FMT, you may either introduce a sub-interface without the generic exception or just implement them within their own class outside java.lang. *2) Generic type for embedded expressions* While I don't see the value of the existing type parameters, I think it would be helpful to have a generic type for the embedded expressions. In contrast to the existing type parameters, such a type parameter would extend the capabilities. It would be possible to have type-safe inputs for processors, which is not possible with the current JEP. public interface StringTemplate ??? ... ??? public interface Processor { ??????? Object process(StringTemplate st) throws Throwable; ??? } ??? ... } *3) Let STR and FMT keep the indentation * I was wondering if STR and FMT should keep the intention when the arguments contain line breaks. Most usages are probably not indented or only use arguments without line breaks. However, whenever I encountered a situation where I wanted to insert a multiline string into an indented line, I always wanted to keep the intention. A potential implementation could basically copy all the whitespace characters directly after the most recent line break in the fragments and insert them behind each line break of the template argument. String multilineString = """ First line. Second line. """; System.out.println(""" Output: \{multilineString} ... """); I have to say I am myself a bit skeptical as this would noticeably increase the complexity. However, I cannot think of a scenario where you don't want this to happen. (I could imagine that I would otherwise implement such a processor in almost all my projects and end up never using STR.) *4) Purpose of StringTemplate.interpolate()?* And finally, although not very important, I am wondering about the usefulness of StringTemplate.interpolate. In the rare case when you actually need this functionality, you can also call STR.process(template). There is also template.process(STR) for some reason. For toString()-like use cases, we already have StringTemplate.toString(StringTemplate). I don't think that StringTemplate.interpolate() would be a good fit in such cases. And again, you can otherwise still call STR.process(...). That was all. I am happy for any reply. PS: I find it interesting that the JEP does not mention tagged templates of JavaScript, although the concepts look quite similar. Best Regards, Johannes -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Sun Oct 29 07:14:18 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Sun, 29 Oct 2023 08:14:18 +0100 Subject: Type parameters and other topics regarding String Templates In-Reply-To: References: Message-ID: Hello! On Sun, Oct 29, 2023 at 2:16?AM Johannes Spangenberg < johannes.spangenberg at hotmail.de> wrote: > Hello, I recently thought about String Templates and just read the new JEP > 459 for the second preview. I have four comments about the current JEP, > more or less ordered by importance. > > *1) Unusual generic types for exceptions and return values* > The convenience of generic types is that you don't always need to spell out or even publicly declare the concrete processor type. However, I agree that generic types is not what strictly needed to be mentioned in the specification. We can have // This type is mentioned in the specification as the root type of string template // clients may implement it directly using more specific return type and exception declaration interface Processor { Object process(StringTemplate template) throw Throwable; } // This is just a convenient generic subtype, not mentioned in the specification // for people who don't want to publicly declare their own types for processors @FunctionalInterface interface ParameterizedProcessor extends Processor { R process(StringTemplate template) throws E; static ParameterizedProcessor of(Function process) { return process::apply; } } The problem is more with the naming: how to name these two things to clearly show the difference between them? > The generic types as specified by JEP 459 look like remnants to me. Since > template expressions are syntactic sugar for method calls, we should just > use the type of the process method, as was introduced by this JEP. However, > the generic types were somehow left in the JEP. Is there a good reason for > that? > > For example, java.lang.AutoCloseable does not have a generic type for the > exception. The only advantage I see is that you can implement processors > using Lambdas without introducing a functional interface. How often do you > have a processor that is trivial enough that you see the benefit of using a > lambda? Even the implementation of STR takes multiple lines. If somebody > needs a functional interface, it is trivial to create one yourself. > > In my opinion, the noise introduced by the template parameters greatly > overshadows the advantage of lambdas without having to declare a functional > interface first. > On the other hand, you may not have such noise even now, if you declare your own concrete types that inherit the generic processor. > For RAW, STR, and FMT, you may either introduce a sub-interface without > the generic exception or just implement them within their own class outside > java.lang. > > *2) Generic type for embedded expressions* > > While I don't see the value of the existing type parameters, I think it > would be helpful to have a generic type for the embedded expressions. In > contrast to the existing type parameters, such a type parameter would > extend the capabilities. It would be possible to have type-safe inputs for > processors, which is not possible with the current JEP. > > public interface StringTemplate > ... > public interface Processor { > Object process(StringTemplate st) throws Throwable; > } > ... > } > > This covers only a limited scenario when all the embedded expressions have the same type. Can you imagine a particular use-case for that scenario? > > *3) Let STR and FMT keep the indentation * > > I was wondering if STR and FMT should keep the intention when the > arguments contain line breaks. Most usages are probably not indented or > only use arguments without line breaks. However, whenever I encountered a > situation where I wanted to insert a multiline string into an indented > line, I always wanted to keep the intention. A potential implementation > could basically copy all the whitespace characters directly after the most > recent line break in the fragments and insert them behind each line break > of the template argument. > > String multilineString = """ > First line. > Second line. > """; > System.out.println(""" > Output: > > \{multilineString} > > ... > """); > > I have to say I am myself a bit skeptical as this would noticeably > increase the complexity. However, I cannot think of a scenario where you > don't want this to happen. (I could imagine that I would otherwise > implement such a processor in almost all my projects and end up never using > STR.) > You can use instead STR.""" Output: \{multilineString.indent(4)} """ Which would be more explicit, thus less magical. In general, it's expected that a properly written program would only rarely use STR processor (especially for multiline output), preferring more domain-specific processors depending on how the resulting string should be interpreted. > *4) Purpose of StringTemplate.interpolate()?* > > And finally, although not very important, I am wondering about the > usefulness of StringTemplate.interpolate. In the rare case when you > actually need this functionality, you can also call STR.process(template). > There is also template.process(STR) for some reason. > For toString()-like use cases, we already have > StringTemplate.toString(StringTemplate). I don't think that > StringTemplate.interpolate() would be a good fit in such cases. And > again, you can otherwise still call STR.process(...). > > > That was all. I am happy for any reply. > > PS: I find it interesting that the JEP does not mention tagged templates > of JavaScript, although the concepts look quite similar. > > Best Regards, > Johannes > -------------- next part -------------- An HTML attachment was scrubbed... URL: From johannes.spangenberg at hotmail.de Sun Oct 29 10:56:58 2023 From: johannes.spangenberg at hotmail.de (Johannes Spangenberg) Date: Sun, 29 Oct 2023 11:56:58 +0100 Subject: Type parameters and other topics regarding String Templates In-Reply-To: References: Message-ID: > Hello! Thanks for the reply! > *1) Unusual generic types for exceptions and return values* > > The convenience of generic types is that you don't always need to > spell out or even publicly declare the concrete processor type. > However, I agree that generic types is not what strictly needed to be > mentioned in the specification. > I do not see the problem with spelling out the concrete processor type. Based on my experience, I expect it to be better for readability to create a separate class anyway. However, I agree that it is non-ideal that you have to do so in an exported package. Especially considering that it is not trivial to restrict access to the constructor when the field and the type of the processor are in different packages. Maybe you are right. > We can have > > // This type is mentioned in the specification as the root type of > string?template > // clients may implement it directly using more specific return type > and exception declaration > interface Processor { > ? Object process(StringTemplate template) throw Throwable; > } > > // This is just a convenient generic subtype, not mentioned in the > specification > // for people who don't want to publicly declare their own types for > processors > @FunctionalInterface > interface ParameterizedProcessor extends > Processor { > ? R process(StringTemplate template) throws E; > > ? static ParameterizedProcessor > of(Function process) { > ? ? return process::apply; > ? } > } > > The problem is more with the naming: how to name these two things to > clearly show the difference between them? I am reluctant about this. I would probably continue to implement the generic interface just because it is more specific. I would like to avoid a scenario where I did not implement a more specific interface out of ignorance, and some other library is now expecting that interface. I guess it is better not to open up this room for inconsistency between processors. > > The generic types as specified by JEP 459 look like remnants to > me. Since template expressions are syntactic sugar for method > calls, we should just use the type of the process method, as was > introduced by this JEP. However, the generic types were somehow > left in the JEP. Is there a good reason for that? > > For example, java.lang.AutoCloseable does not have a generic type > for the exception. The only advantage I see is that you can > implement processors using Lambdas without introducing a > functional interface. How often do you have a processor that is > trivial enough that you see the benefit of using a lambda? Even > the implementation of STR takes multiple lines. If somebody needs > a functional interface, it is trivial to create one yourself. > > In my opinion, the noise introduced by the template parameters > greatly overshadows the advantage of lambdas without having to > declare a functional interface first. > > > On the other hand, you may not have such noise even now, if you > declare your own concrete types that inherit the generic processor. You may be right. > For RAW, STR, and FMT, you may either introduce a sub-interface > without the generic exception or just implement them within their > own class outside java.lang. > > *2) Generic type for embedded expressions* > > While I don't see the value of the existing type parameters, I > think it would be helpful to have a generic type for the embedded > expressions. In contrast to the existing type parameters, such a > type parameter would extend the capabilities. It would be possible > to have type-safe inputs for processors, which is not possible > with the current JEP. > > public interface StringTemplate > ??? ... > ??? public interface Processor { > ??????? Object process(StringTemplate st) throws Throwable; > ??? } > ??? ... > } > > > This covers only a limited scenario when all the embedded expressions > have the same type. Can you imagine a particular use-case for that > scenario? I am aware that this limitation will prevent most processors from using a more specific type. I am mostly just reluctant to give up type-safety. You might be able to use typed parameters in some more advanced use cases. I could imagine to define a Swing Layout using a template string and a DSL. The DSL might be somewhat similar to grid-template in CSS. All the template arguments would need to be Swing components. Container contentPane = ...; contentPane.setLayout(LAYOUT.""" \{logo} \{toolbar} \{userinfo} fit-content \{navigation} \{content} \{content} minmax(100px, auto) \{footer} \{footer} \{footer} fit-content 100px minmax(100px, auto) fit-content """); I would also be in favor of getting union types. I was often struggling with this limitation independent from String Templates. Anyway, unions are different topic. (If you are aware of any active discussions or previous attempts, I would be happy to know. I would also have an idea how to represent them in Bytecode, but not sure how well the problem itself was discussed yet.) > *3) Let STR and FMT keep the indentation > * > > I was wondering if STR and FMT should keep the intention when the > arguments contain line breaks. Most usages are probably not > indented or only use arguments without line breaks. However, > whenever I encountered a situation where I wanted to insert a > multiline string into an indented line, I always wanted to keep > the intention. A potential implementation could basically copy all > the whitespace characters directly after the most recent line > break in the fragments and insert them behind each line break of > the template argument. > > String multilineString = """ > First line. > Second line. > """; > System.out.println(""" > Output: > > \{multilineString} > > ... > """); > > I have to say I am myself a bit skeptical as this would noticeably > increase the complexity. However, I cannot think of a scenario > where you don't want this to happen. (I could imagine that I would > otherwise implement such a processor in almost all my projects and > end up never using STR.) > > You can use instead > STR.""" > Output: > \{multilineString.indent(4)} > """ Good point. I would probably still prefer to implement my own template processor, but String.indent(int) already makes it less annoying if you don't have one. It might still be good to have an option or flag for FMT, but that would be out of scope of this JEP. > Which would be more explicit, thus less magical. In general, it's > expected that a properly written program would only rarely use STR > processor (especially for multiline output), preferring more > domain-specific processors depending on how the resulting string > should be interpreted. > > *4) Purpose of StringTemplate.interpolate()?* > > And finally, although not very important, I am wondering about the > usefulness of StringTemplate.interpolate. In the rare case when > you actually need this functionality, you can also call > STR.process(template). There is also template.process(STR) for > some reason. > > For toString()-like use cases, we already have > StringTemplate.toString(StringTemplate). I don't think that > StringTemplate.interpolate() would be a good fit in such cases. > And again, you can otherwise still call STR.process(...). > > > That was all. I am happy for any reply. > > PS: I find it interesting that the JEP does not mention tagged > templates of JavaScript, although the concepts look quite similar. > > Best Regards, > Johannes > -------------- next part -------------- An HTML attachment was scrubbed... URL: From johannes.spangenberg at hotmail.de Sun Oct 29 15:33:55 2023 From: johannes.spangenberg at hotmail.de (Johannes Spangenberg) Date: Sun, 29 Oct 2023 16:33:55 +0100 Subject: [String Templates] Allow template processors to preprocess fragments to improve performance Message-ID: Following my email from yesterday, I have had another unrelated through. I would like to reference JEP 459: > To make it more efficient, we could memoize this processor by > compiling the template's fragments into a JSONObject with placeholder > values and caching the result. If the next invocation of the processor > uses the same fragments [...] Assuming that Template Strings are predominantly used on final static fields, we could greatly simplify the process of memoizing the preprocessed fragments by using Invoke Dynamic. However, this would require to change the interfaces. Here is an example of how it might look: public interface StringTemplate { Factory> STR = ...; R process(List

args) throws E; interface Factory> { T create(List fragments); } } Assuming the same factory is used, the method Factory.create(List) would be called only once per call site. Only StringTemplate.process(List) is invoked each time the code is executed. The implementation of the Metafactory should be relatively straightforward. The Invoke Dynamic instruction would look something like this: INVOKEDYNAMIC process(Ljava/lang/StringTemplate$Factory;Ljava/time/LocalDate;Ljava/lang/String;Ljava/lang/Integer;)Ljava/lang/String; [ // handle kind 0x6 : INVOKESTATIC java/lang/invoke/StringTemplateMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/String;)Ljava/lang/invoke/CallSite; // arguments: "Date: ", "\nString: ", "\nInteger: ", "" ] I have some reservations, as the factory can be any arbitrary expression. There is nothing preventing users from creating a new factory on every call. We would need to decide on a caching strategy or only enable this optimization when used with final static fields. The Metafactory would also make String Templates more magic, as they would no-longer be just syntactic sugar. On the other hand, I can imagine this significantly improving the performance for complex template processors. I believe this will be my last suggestion concerning String Templates. I am looking forward to using String Templates at work with Java 25, whether my proposed changes are incorporated or not. Best Regards, Johannes Spangenberg -------------- next part -------------- An HTML attachment was scrubbed... URL: From tzengshinfu at gmail.com Tue Oct 31 02:10:16 2023 From: tzengshinfu at gmail.com (tzengshinfu) Date: Tue, 31 Oct 2023 10:10:16 +0800 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: Thank you, Anatoly, for informing me about `IntegerCache`. As a developer experienced in C# and JavaScript/TypeScript, I am currently responsible for migrating our in-house systems from C# to Java. I also have the responsibility of training and sharing knowledge with others. I was surprised by the following result in Java: ```java Integer int1 = 100; Integer int2 = 100; Integer int3 = int1 + 100; Integer int4 = int2 + 100; out.println(int3 == int4); // false ``` This is different from how it works in C#: ```C# Int32 int1 = 100; Int32 int2 = 100; Int32 int3 = int1 + 100; Int32 int4 = int2 + 100; Console.Write(int3 == int4); // True ``` Now, I understand that Java's design philosophy (thanks to Brian for patiently explaining) leads to this behavior. I will make an effort to understand how this works under the hood and include it in my Java FAQ documentation. My initial thoughts and suggestions were aimed at colleagues who are also transitioning from other programming languages and newcomers to Java who bring their language experiences. Naturally, they might find the unexpected results in certain use cases, and I hope to find ways to make this learning curve smoother without disrupting the existing framework. Without breaking the current architecture, both Solution A (changing String1 == String2 to String::equals()) and Solution B (introducing a new string/str class to replace the String class) are unfeasible (thanks to Nathan for preventing "I think it's rarely used, but in reality, it's not, and everything breaks"). Can I propose a Solution C? Solution C: Introduce the === and !== operators for value comparison (equivalent to Object::equals()). For example: ```java if (Integer4 === Integer1 + Integer2 + Integer3) { // Do something... } ``` Would be equivalent to: ```java if (Integer4.equals(Integer1 + Integer2 + Integer3)) { // Do something... } ``` >From a visual perspective, I believe that the === and !== operators can reduce the chances of typos and make the code look cleaner, with fewer parentheses compared to using equals(). Sometimes, symbols can convey meaning more clearly than text. This idea stems from my experience with Delphi, where the use of keywords like begin and end mixed with variable declarations and expressions made code difficult to read in a plain text editor, despite IDE features like highlighting and automatic formatting. Java's use of {} solved this issue. However, there are also counterexamples, such as Rust's code, where excessive symbols may contribute to a steep learning curve. /* GET BETTER EVERY DAY */ -------------- next part -------------- An HTML attachment was scrubbed... URL: From jens at lidestrom.se Tue Oct 31 07:33:16 2023 From: jens at lidestrom.se (=?UTF-8?Q?Jens_Lidestr=C3=B6m?=) Date: Tue, 31 Oct 2023 08:33:16 +0100 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: Remember that C# has its own quirks regarding the behaviour of ==. Example of the behaviour in C#: ``` string s1 = new String("1"); string s2 = new String("1"); object o1 = s1; object o2 = s2; Console.WriteLine(s1 == s2); // True, overload for string kicks in Console.WriteLine(o1 == o2); // False, reference equality! int? i1 = 1; int? i2 = 1; object o1 = i1; object o2 = i2; Console.WriteLine(i1 == i2); // True, overload for Nullable kicks in Console.WriteLine(o1 == o2); // False, reference equality! ``` That is, in C# the behaviour of == varies for the same objects, depending on what types the references have. Both approaches have their advantages and disadvantages. But to me Java's approach seems more sensible: For reference types == always means reference equality. Best regards, Jens Lidestr?m On 2023-10-31 03:10, tzengshinfu wrote: > Thank you, Anatoly, for informing me about `IntegerCache`. > As a developer experienced in C# and JavaScript/TypeScript, I am > currently responsible for migrating our in-house systems from C# to > Java. > I also have the responsibility of training and sharing knowledge with > others. > > I was surprised by the following result in Java: > > ```java > Integer int1 = 100; > Integer int2 = 100; > Integer int3 = int1 + 100; > Integer int4 = int2 + 100; > out.println(int3 == int4); // false > ``` > > This is different from how it works in C#: > > ```C# > Int32 int1 = 100; > Int32 int2 = 100; > Int32 int3 = int1 + 100; > Int32 int4 = int2 + 100; > Console.Write(int3 == int4); // True > ``` > > Now, I understand that Java's design philosophy (thanks to Brian for > patiently explaining) leads to this behavior. > I will make an effort to understand how this works under the hood and > include it in my Java FAQ documentation. > My initial thoughts and suggestions were aimed at colleagues who are > also transitioning from other programming languages and newcomers to > Java who bring their language experiences. > Naturally, they might find the unexpected results in certain use cases, > and I hope to find ways to make this learning curve smoother without > disrupting the existing framework. > > Without breaking the current architecture, both Solution A (changing > String1 == String2 to String::equals()) and Solution B (introducing a > new string/str class to replace the String class) are unfeasible > (thanks to Nathan for preventing "I think it's rarely used, but in > reality, it's not, and everything breaks"). > > Can I propose a Solution C? > > Solution C: Introduce the === and !== operators for value comparison > (equivalent to Object::equals()). > > For example: > > ```java > if (Integer4 === Integer1 + Integer2 + Integer3) { > // Do something... > } > ``` > > Would be equivalent to: > > ```java > if (Integer4.equals(Integer1 + Integer2 + Integer3)) { > // Do something... > } > ``` > > From a visual perspective, I believe that the === and !== operators can > reduce the chances of typos and make the code look cleaner, with fewer > parentheses compared to using equals(). > Sometimes, symbols can convey meaning more clearly than text. This idea > stems from my experience with Delphi, where the use of keywords like > begin and end mixed with variable declarations and expressions made > code difficult to read in a plain text editor, despite IDE features > like highlighting and automatic formatting. Java's use of {} solved > this issue. > However, there are also counterexamples, such as Rust's code, where > excessive symbols may contribute to a steep learning curve. > > /* GET BETTER EVERY DAY */ From pedro.lamarao at prodist.com.br Tue Oct 31 11:58:26 2023 From: pedro.lamarao at prodist.com.br (=?UTF-8?Q?Pedro_Lamar=C3=A3o?=) Date: Tue, 31 Oct 2023 08:58:26 -0300 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: Also, this behaviour that may seem weird from programmers coming from C# is quite familiar to those coming from C++. int i1 = 1; int i2 = 1; int* p1 = &i1; int* p2 = &i2; std::cout << (i1 == i2) << std::endl; // prints true std::cout << (p1 == p2) << std::endl; // prints false Atte. Pedro. Em ter., 31 de out. de 2023 ?s 04:34, Jens Lidestr?m escreveu: > Remember that C# has its own quirks regarding the behaviour of ==. > > Example of the behaviour in C#: > > ``` > string s1 = new String("1"); > string s2 = new String("1"); > object o1 = s1; > object o2 = s2; > Console.WriteLine(s1 == s2); // True, overload for string kicks in > Console.WriteLine(o1 == o2); // False, reference equality! > > int? i1 = 1; > int? i2 = 1; > object o1 = i1; > object o2 = i2; > Console.WriteLine(i1 == i2); // True, overload for Nullable kicks > in > Console.WriteLine(o1 == o2); // False, reference equality! > ``` > > That is, in C# the behaviour of == varies for the same objects, > depending on what types the references have. > > Both approaches have their advantages and disadvantages. But to me > Java's approach seems more sensible: For reference types == always means > reference equality. > > Best regards, > Jens Lidestr?m > > On 2023-10-31 03:10, tzengshinfu wrote: > > > Thank you, Anatoly, for informing me about `IntegerCache`. > > As a developer experienced in C# and JavaScript/TypeScript, I am > > currently responsible for migrating our in-house systems from C# to > > Java. > > I also have the responsibility of training and sharing knowledge with > > others. > > > > I was surprised by the following result in Java: > > > > ```java > > Integer int1 = 100; > > Integer int2 = 100; > > Integer int3 = int1 + 100; > > Integer int4 = int2 + 100; > > out.println(int3 == int4); // false > > ``` > > > > This is different from how it works in C#: > > > > ```C# > > Int32 int1 = 100; > > Int32 int2 = 100; > > Int32 int3 = int1 + 100; > > Int32 int4 = int2 + 100; > > Console.Write(int3 == int4); // True > > ``` > > > > Now, I understand that Java's design philosophy (thanks to Brian for > > patiently explaining) leads to this behavior. > > I will make an effort to understand how this works under the hood and > > include it in my Java FAQ documentation. > > My initial thoughts and suggestions were aimed at colleagues who are > > also transitioning from other programming languages and newcomers to > > Java who bring their language experiences. > > Naturally, they might find the unexpected results in certain use cases, > > and I hope to find ways to make this learning curve smoother without > > disrupting the existing framework. > > > > Without breaking the current architecture, both Solution A (changing > > String1 == String2 to String::equals()) and Solution B (introducing a > > new string/str class to replace the String class) are unfeasible > > (thanks to Nathan for preventing "I think it's rarely used, but in > > reality, it's not, and everything breaks"). > > > > Can I propose a Solution C? > > > > Solution C: Introduce the === and !== operators for value comparison > > (equivalent to Object::equals()). > > > > For example: > > > > ```java > > if (Integer4 === Integer1 + Integer2 + Integer3) { > > // Do something... > > } > > ``` > > > > Would be equivalent to: > > > > ```java > > if (Integer4.equals(Integer1 + Integer2 + Integer3)) { > > // Do something... > > } > > ``` > > > > From a visual perspective, I believe that the === and !== operators can > > reduce the chances of typos and make the code look cleaner, with fewer > > parentheses compared to using equals(). > > Sometimes, symbols can convey meaning more clearly than text. This idea > > stems from my experience with Delphi, where the use of keywords like > > begin and end mixed with variable declarations and expressions made > > code difficult to read in a plain text editor, despite IDE features > > like highlighting and automatic formatting. Java's use of {} solved > > this issue. > > However, there are also counterexamples, such as Rust's code, where > > excessive symbols may contribute to a steep learning curve. > > > > /* GET BETTER EVERY DAY */ -------------- next part -------------- An HTML attachment was scrubbed... URL: From hjohn at xs4all.nl Tue Oct 31 13:42:18 2023 From: hjohn at xs4all.nl (John Hendrikx) Date: Tue, 31 Oct 2023 13:42:18 +0000 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: I think this comparison is not very valid. `Integer` is not the type you an experienced Java developer would use for this, you'd use `int` which can't be set to `null`, just like `Int32` in C# can't be set to `null`. Generally, use of `Integer` like you do in your example is not recommended and often a sign that the developer is not yet that familiar with Java. The only places I'd expect to see `Integer` is in generic declarations (like List) or at the input/output layers of your application to indicate a value can be an integer OR null. Usage like this would immediately (and safely) be replaced with `int` (doing calculations on them means they can't be null so `int` is the correct type to use). ```java int int1 = 100; int int2 = 100; int int3 = int1 + 100; int int4 = int2 + 100; out.println(int3 == int4); // true! ``` --John ------ Original Message ------ >From "tzengshinfu" To amber-dev at openjdk.org Date 31/10/2023 03:10:16 Subject Re: Is there a possibility of the string equality operator (==) being fixed? >Thank you, Anatoly, for informing me about `IntegerCache`. >As a developer experienced in C# and JavaScript/TypeScript, I am >currently responsible for migrating our in-house systems from C# to >Java. >I also have the responsibility of training and sharing knowledge with >others. > >I was surprised by the following result in Java: > >```java >Integer int1 = 100; >Integer int2 = 100; >Integer int3 = int1 + 100; >Integer int4 = int2 + 100; >out.println(int3 == int4); // false >``` > >This is different from how it works in C#: > >```C# >Int32 int1 = 100; >Int32 int2 = 100; >Int32 int3 = int1 + 100; >Int32 int4 = int2 + 100; >Console.Write(int3 == int4); // True >``` > >Now, I understand that Java's design philosophy (thanks to Brian for >patiently explaining) leads to this behavior. >I will make an effort to understand how this works under the hood and >include it in my Java FAQ documentation. >My initial thoughts and suggestions were aimed at colleagues who are >also transitioning from other programming languages and newcomers to >Java who bring their language experiences. >Naturally, they might find the unexpected results in certain use cases, >and I hope to find ways to make this learning curve smoother without >disrupting the existing framework. > >Without breaking the current architecture, both Solution A (changing >String1 == String2 to String::equals()) and Solution B (introducing a >new string/str class to replace the String class) are unfeasible >(thanks to Nathan for preventing "I think it's rarely used, but in >reality, it's not, and everything breaks"). > >Can I propose a Solution C? > >Solution C: Introduce the === and !== operators for value comparison >(equivalent to Object::equals()). > >For example: > >```java >if (Integer4 === Integer1 + Integer2 + Integer3) { > // Do something... >} >``` > >Would be equivalent to: > >```java >if (Integer4.equals(Integer1 + Integer2 + Integer3)) { > // Do something... >} >``` > >From a visual perspective, I believe that the === and !== operators can >reduce the chances of typos and make the code look cleaner, with fewer >parentheses compared to using equals(). >Sometimes, symbols can convey meaning more clearly than text. This idea >stems from my experience with Delphi, where the use of keywords like >begin and end mixed with variable declarations and expressions made >code difficult to read in a plain text editor, despite IDE features >like highlighting and automatic formatting. Java's use of {} solved >this issue. >However, there are also counterexamples, such as Rust's code, where >excessive symbols may contribute to a steep learning curve. > >/* GET BETTER EVERY DAY */ > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Oct 31 14:05:36 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 31 Oct 2023 10:05:36 -0400 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: <4c35c256-1217-73f4-f5e8-8a187dab2959@oracle.com> > > Can I propose a Solution C? > > Solution C: Introduce the === and !== operators for value comparison > (equivalent to Object::equals()). As will not be surprising, this is not a new suggestion either. JavaScript had good success with ===, but they started out in a very different position; == was so catastrophically difficult to reason about that it was reasonable to tell users to basically forget about ==.? Moving to === was virtually a forced move for them.? We don't have that problem; == has a firm, and learnable foundation in Java. But, it gets worse. Inventing === as a synonym for == is purely a syntactic hack; there are no useful new semantics here.? And because both == and === will be valid on all types, people will still make mistakes that are going to be hard to catch -- arguably harder, because they look so much alike. But here's the killer for this idea; .equals() is asymmetric; calling a.equals(b) can invoke different code than b.equals(a), if they have different implementation classes and both refine equals().? Which means `===` is not commutative, and which you put on the left matters.? But this is not how we expect equality comparison operators to work. From kan.izh at gmail.com Tue Oct 31 15:21:35 2023 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Tue, 31 Oct 2023 15:21:35 +0000 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: I believe the thing you are asking for is contradicting the Java language design philosophy. Java is a very simple language, relatively low-level, like a Pure C - everything means quite literally, no hidden stuff. The == is a language-level and means a very simple thing. In other languages like C++ and C# the == could also be a user-defined operator (a method with a special syntax) and could do unexpected things. Hence that quirky behaviour with the object comparison as demonstrated in the Jens email. There are few compromises which allow to make code more readable, like auto-boxing. Even this simple thing is introducing a havoc so you have got confused why "1 == 1" but "200 != 200" for the Integer type. Hence it is a good thing to keep the design as simple as possible (but not simpler!). If you don't like explicit (unfortunately more verbose as a consequence) syntax of Java like Objects.equals(str1, str2), and you are after cooler languages with more features you could look into other JVM-languages like Scala or Kotlin which do == and other things differently. On Tue, 31 Oct 2023 at 02:11, tzengshinfu wrote: > Thank you, Anatoly, for informing me about `IntegerCache`. > As a developer experienced in C# and JavaScript/TypeScript, I am currently > responsible for migrating our in-house systems from C# to Java. > I also have the responsibility of training and sharing knowledge with > others. > > I was surprised by the following result in Java: > > ```java > Integer int1 = 100; > Integer int2 = 100; > Integer int3 = int1 + 100; > Integer int4 = int2 + 100; > out.println(int3 == int4); // false > ``` > > This is different from how it works in C#: > > ```C# > Int32 int1 = 100; > Int32 int2 = 100; > Int32 int3 = int1 + 100; > Int32 int4 = int2 + 100; > Console.Write(int3 == int4); // True > ``` > > Now, I understand that Java's design philosophy (thanks to Brian for > patiently explaining) leads to this behavior. > I will make an effort to understand how this works under the hood and > include it in my Java FAQ documentation. > My initial thoughts and suggestions were aimed at colleagues who are also > transitioning from other programming languages and newcomers to Java who > bring their language experiences. > Naturally, they might find the unexpected results in certain use cases, > and I hope to find ways to make this learning curve smoother without > disrupting the existing framework. > > Without breaking the current architecture, both Solution A (changing > String1 == String2 to String::equals()) and Solution B (introducing a new > string/str class to replace the String class) are unfeasible (thanks to > Nathan for preventing "I think it's rarely used, but in reality, it's not, > and everything breaks"). > > Can I propose a Solution C? > > Solution C: Introduce the === and !== operators for value comparison > (equivalent to Object::equals()). > > For example: > > ```java > if (Integer4 === Integer1 + Integer2 + Integer3) { > // Do something... > } > ``` > > Would be equivalent to: > > ```java > if (Integer4.equals(Integer1 + Integer2 + Integer3)) { > // Do something... > } > ``` > > From a visual perspective, I believe that the === and !== operators can > reduce the chances of typos and make the code look cleaner, with fewer > parentheses compared to using equals(). > Sometimes, symbols can convey meaning more clearly than text. This idea > stems from my experience with Delphi, where the use of keywords like begin > and end mixed with variable declarations and expressions made code > difficult to read in a plain text editor, despite IDE features like > highlighting and automatic formatting. Java's use of {} solved this issue. > However, there are also counterexamples, such as Rust's code, where > excessive symbols may contribute to a steep learning curve. > > /* GET BETTER EVERY DAY */ > > > -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Oct 31 16:32:16 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 31 Oct 2023 12:32:16 -0400 Subject: Is there a possibility of the string equality operator (==) being fixed? In-Reply-To: References: Message-ID: I think this whole discussions illustrates a deeper point about language design, and how simple-seeming "fixes" turn out to be complicated. Tzengshifu arrived at these thoughts by coming from another language with an existing codebase, and finding that users stumble when things are "mostly the same but a little different."? From there, it is easy to leap to "so, something must be broken", and from there, easier still to leap to "solutions". But the reality is that languages are (hopefully) organic wholes; even when one language adopts a feature "inspired by" a feature in another language, it is more like an organ transplant than buying a new shirt, because invariably each language feature connects to many other features.? In order for a transplant to be successful, all the vessels have to be carefully reconnected (to the right things.) The story of autoboxing is a cautionary tale; one the one hand, it is convenient most of the time, but the mismatch between "primitives compare by value, objects compare by reference" bites us when we try to blur this distinction.? This is another flavor of the "C# equality depends on static types"; it's a visible seam in the carpet that most of the time we can safely navigate, but every once in a while we trip over. On 10/31/2023 11:21 AM, Anatoly Kupriyanov wrote: > I believe the thing you are asking for is contradicting the Java > language design philosophy. Java is a very simple language, relatively > low-level, like a Pure C - everything means quite literally, no hidden > stuff. The == is a language-level and means a very simple thing. > > In other languages like C++ and C# the == could also be a user-defined > operator (a method with a special syntax) and could do unexpected > things. Hence that quirky behaviour with the object comparison as > demonstrated in the Jens email. > > There are few compromises which allow to make code more readable, like > auto-boxing. Even this simple thing is introducing a havoc so you have > got confused why "1 == 1" but "200 != 200" for the Integer type. > Hence it is a good thing to keep the design as simple as possible (but > not simpler!). > > If you don't like explicit (unfortunately more verbose as a > consequence) syntax of Java like Objects.equals(str1, str2), and you > are after cooler languages with more features you could look into > other JVM-languages like Scala or Kotlin which do == and other things > differently. > > > On Tue, 31 Oct 2023 at 02:11, tzengshinfu wrote: > > Thank you, Anatoly, for informing me about `IntegerCache`. > As a developer experienced in C# and JavaScript/TypeScript, I am > currently responsible for migrating our in-house systems from C# > to Java. > I also have the responsibility of training and sharing knowledge > with others. > > I was surprised by the following result in Java: > > ```java > Integer int1 = 100; > Integer int2 = 100; > Integer int3 = int1 + 100; > Integer int4 = int2 + 100; > out.println(int3 == int4); // false > ``` > > This is different from how it works in C#: > > ```C# > Int32 int1 = 100; > Int32 int2 = 100; > Int32 int3 = int1 + 100; > Int32 int4 = int2 + 100; > Console.Write(int3 == int4); // True > ``` > > Now, I understand that Java's design philosophy (thanks to Brian > for patiently explaining) leads to this behavior. > I will make an effort to understand how this works under the hood > and include it in my Java FAQ documentation. > My initial thoughts and suggestions were aimed at colleagues who > are also transitioning from other programming languages and > newcomers to Java who bring their language experiences. > Naturally, they might find the unexpected results in certain use > cases, and I hope to find ways to make this learning curve > smoother without disrupting the existing framework. > > Without breaking the current architecture, both Solution A > (changing String1 == String2 to String::equals()) and Solution B > (introducing a new string/str class to replace the String class) > are unfeasible (thanks to Nathan for preventing "I think it's > rarely used, but in reality, it's not, and everything breaks"). > > Can I propose a Solution C? > > Solution C: Introduce the === and !== operators for value > comparison (equivalent to Object::equals()). > > For example: > > ```java > if (Integer4 === Integer1 + Integer2 + Integer3) { > ? ? // Do something... > } > ``` > > Would be equivalent to: > > ```java > if (Integer4.equals(Integer1 + Integer2 + Integer3)) { > ? ? // Do something... > } > ``` > > From a visual perspective, I believe that the === and !== > operators can reduce the chances of typos and make the code look > cleaner, with fewer parentheses compared to using equals(). > Sometimes, symbols can convey meaning more clearly than text. This > idea stems from my experience with Delphi, where the use of > keywords like begin and end mixed with variable declarations and > expressions made code difficult to read in a plain text editor, > despite IDE features like highlighting and automatic formatting. > Java's use of {} solved this issue. > However, there are also counterexamples, such as Rust's code, > where excessive symbols may contribute to a steep learning curve. > > /* GET BETTER EVERY DAY */ > > > > > -- > WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: