From tzengshinfu at gmail.com Thu Apr 10 04:05:56 2025 From: tzengshinfu at gmail.com (tzengshinfu) Date: Thu, 10 Apr 2025 12:05:56 +0800 Subject: Bridging Java Generations: Making Syntax Intuitive for All Developers Message-ID: Hi, folks: Although Java has recently adopted many modern features, a lot of legacy syntax still remains in the codebase due to concerns about backward compatibility and semantic consistency. In our company, this often means that whenever new developers join, I have to re-explain some core concepts of Java?especially to those who have never worked with it before. For example, newcomers usually notice that you can concatenate strings using String1 + String2 (to be honest, it?s this syntactic sugar that makes ?Problem 1? harder to explain), and then immediately ask the FAQ ?Problem 1?: ?Why can?t we compare strings with String1 == String2?? This dives into the concept of values vs. objects. In other languages, this issue is often hidden behind syntactic sugar. But in Java, new developers often have to ?lift the hood? and look into the internals to truly understand it. Although String is implemented as an object, many languages still allow value comparisons using == or ===. Beginners or seasoned developers from other languages often misinterpret strings as simple value types and may not feel the need to dive deeper. Personally, I believe the real confusion arises from the fact that the same operator == compares values for primitive types but compares references for classes. If we truly want to compare references, it should be done through an object method, like Object.addressEquals(Object1, Object2) (assuming such a method exists). After all, operators are supposed to be used for value comparison. Even if Java introduces JEP 401: Value Classes and Objects, I bet newcomers will still ask, ?Why can I use == to compare what is obviously an object?? Although the official recommendation remains to use .equals(), let?s be honest?many will still use ==, because, well... This: ``` if (USDCurrency1 == USDCurrency2) { USDCurrency3 = USDCurrency1 + USDCurrency2; } ``` is obviously much more readable and intuitive than: ``` if (USDCurrency1.equals(USDCurrency2)) { USDCurrency3 = USDCurrency1.add(USDCurrency2); } ``` Sure, methods provide clearer semantics, but sometimes simple operations are more immediately understandable with operators. Operators visually separate arguments more intuitively, while methods with long names can blur the lines between method and arguments, especially when the names get lengthy. ?Problem 2? arises from syntax introduced in different Java eras?such as lambda expressions and switch expressions?which often confuse even seasoned Java developers. Even the latest proposal, JEP 477: Implicitly Declared Classes and Instance Main Methods, helps younger developers learn faster, but at the same time increases the learning curve for veteran developers. And after learning all the shiny new syntax, the younger folks still come back to ?Problem 1?. This made me realize: the later a JEP (Java Enhancement Proposal) is introduced, the less likely it is to bring major syntax changes or optimizations. Introducing new syntax always comes with trade-offs?ensuring backward compatibility and balancing the pros and cons. Take JEP 465: String Templates, for example?a highly anticipated feature across languages, but it was withdrawn due to design issues. Or operator overloading, which would be extremely useful for algorithm and library developers, but continues to be excluded due to the potential for misuse. Syntax evolution?its additions, changes, or simplifications?impacts developers the most. Cleaner syntax can help developers focus on business logic instead of fighting with quirky syntax. It reduces cognitive load, visual fatigue, and even finger strain. If syntax updates can?t make the developer?s life easier, they become handcuffed. And in the AI age, how can Java attract developers from other languages (especially Python) without offering comparable simplicity? But again, every syntax change comes at a cost. This led me to: - Is there a way for both junior and senior developers to work on the same codebase? - Can we make it easier to introduce new syntax into an existing structure? - Can we write and read code as concisely as a script while keeping the strict semantics of Java?hiding implementation details when focusing on business logic, and expanding them on demand? With that thought, I discovered an IntelliJ IDEA plugin: ?Advanced Java Folding?. It enables both new and experienced developers to collaborate on the same codebase without modifying the source code. I forked this plugin, added a quick-input method feature, and renamed it ?Advanced Java Folding Plus?. Here?s how it works: For example, when I type BigDecimal1 + BigDecimal2, the actual code is BigDecimal1.add(BigDecimal2), but the UI shows exactly what I typed. When I type String1 === String2, the actual code is String1.equals(String2), and again, the UI reflects the original input. PS: I currently define === as a pure value comparison to avoid semantic conflict with ==. In the IDE, developers can toggle the display: - Fold verbose syntax to reduce visual clutter - Unfold simplified syntax when more clarity is needed This plugin can serve as a workaround for the lack of operator overloading. In theory, it could support most syntax transformations and even replace certain libraries, like Lombok or Manifold. No disrespect to those libraries, but many ?features? in programming languages are really just syntactic sugar. Even brand-new keywords or syntax in a language are often sugar-coated versions of existing constructs. We all know sugar isn?t exactly healthy?but a little sweetness helps highlight the flavor of business logic. Of course, everything in moderation (although I?ve noticed young developers these days really love their sweets). That said, this is still just an IDE-bound plugin. If the Java ecosystem could abstract new syntax into a layer of isolation? like an automated syntax transformation layer that lets developers from different eras or different languages see code in ?their? version of Java syntax? maybe it could help attract more young developers and developers from other languages into the Java world. Just a personal thought, a wish to help Java appeal to more developers from different backgrounds. /* GET BETTER EVERY DAY */ -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Apr 12 14:39:22 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 12 Apr 2025 10:39:22 -0400 Subject: My experience using Java to do CLI Scripting Message-ID: Hello Amber Dev Team and Kulla Dev Team, (I made a reddit post too, if you prefer to interact there instead -- https://www.reddit.com/r/java/comments/1jx87ys/) The following JEP's have released recently. - JEP 495: Simple Source Files and Instance Main Methods - JEP 330: Launch Single-File Source-Code Programs - JEP 222: jshell: The Java Shell (Read-Eval-Print Loop) These have made it really easy for me to do CLI scripting in Java, as opposed to Bash. However, I've run into some pain points, as I've relied more and more on Java. For starters, the hand off from Java --> Bash is kind of ugly. Bash --> Java is not bad, due to void main(final String[] args), as well as Bash's xargs. But Java --> Bash is ugly, and here is an example demonstrating how/why. ------------------------------ I use AWS CLI to manage my dev environment. It's super powerful, and is all available directly from the CLI, using simple Bash or even CMD. Let's say I use AWS CLI to gather some ad-hoc information about my entire dev environment. How do I manage the multiple handoffs back and forth between AWS CLI and Java? There are no good answers. 1. Store the results into a file, then use JShell/java(c) to process the output file from Bash/AWS CLI. - There's multiple handoffs back and forth between AWS CLI and Java. So, every handoff from Java ---> AWS CLI means generating a new file, thus increasing the complexity and cruft. It's unideal. 2. Use Java's ProcessBuilder and Process classes. - This works, but is heavy-handed. Look at the examples in those links. That is multiple lines of code to represent a single bash command. It does appear to be the idiomatic way, though. 3. Do all upstream processing with AWS CLI in Bash directly, then do only a single handoff to Java, once I have done all I need to with AWS CLI. - This is definitely the least painful, but it also means I don't use much Java at all. And any changes in upstream processing must be done in Bash to avoid handoff headaches from AWS CLI ---> Java. 4. Download the AWS SDK Jar files and just do it all in Java. - Ignoring the fact that some things are much harder to do via the AWS Java SDK's, there's actually some functionality that just isn't available via the Java ones. I'd have to recreate it myself, and it would be a significant lift. Option 4 is best when I am building an application, but for ad-hoc checks that I want to do on the fly (my most common use-case by far), I have been using Option 3. I just wish I could use more Java. It's a *FAR BETTER*tool than Bash, but I can't justify the level of effort for ad-hoc use cases because of the poor hand off from Java --> Bash. And since AWS CLI is only available via Bash/CMD, I'm stuck with a bunch of not-good choices. ------------------------------ CLI Scripting in Java is great, but I wanted to highlight this pain point to spread awareness. Can you relate? -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Sat Apr 12 15:02:05 2025 From: ron.pressler at oracle.com (Ron Pressler) Date: Sat, 12 Apr 2025 15:02:05 +0000 Subject: My experience using Java to do CLI Scripting In-Reply-To: References: Message-ID: <356624F4-6FE4-4D22-92EE-049C1E03472C@oracle.com> Hi. Let?s focus on ProcessBuilder and Process (as I think that?s where you want to focus, and why I think this discussion is more appropriate for core-libs-dev). Can you try to show more concretely what the pain point is in your actual code? The ProcessBuilder example is long because it does multiple things, each of which may or may not be relevant to your use case. That doing five different things requires five lines of code doesn?t help us see where your specific pain point is. ? Ron > On 12 Apr 2025, at 15:39, David Alayachew wrote: > > Hello Amber Dev Team and Kulla Dev Team, > > (I made a reddit post too, if you prefer to interact there instead -- https://www.reddit.com/r/java/comments/1jx87ys/) > The following JEP's have released recently. > ? JEP 495: Simple Source Files and Instance Main Methods > ? JEP 330: Launch Single-File Source-Code Programs > ? JEP 222: jshell: The Java Shell (Read-Eval-Print Loop) > These have made it really easy for me to do CLI scripting in Java, as opposed to Bash. However, I've run into some pain points, as I've relied more and more on Java. > For starters, the hand off from Java --> Bash is kind of ugly. Bash --> Java is not bad, due to void main(final String[] args), as well as Bash's xargs. But Java --> Bash is ugly, and here is an example demonstrating how/why. > I use AWS CLI to manage my dev environment. It's super powerful, and is all available directly from the CLI, using simple Bash or even CMD. > Let's say I use AWS CLI to gather some ad-hoc information about my entire dev environment. How do I manage the multiple handoffs back and forth between AWS CLI and Java? > There are no good answers. > ? Store the results into a file, then use JShell/java(c) to process the output file from Bash/AWS CLI. > ? There's multiple handoffs back and forth between AWS CLI and Java. So, every handoff from Java ---> AWS CLI means generating a new file, thus increasing the complexity and cruft. It's unideal. > ? Use Java's ProcessBuilder and Process classes. > ? This works, but is heavy-handed. Look at the examples in those links. That is multiple lines of code to represent a single bash command. It does appear to be the idiomatic way, though. > ? Do all upstream processing with AWS CLI in Bash directly, then do only a single handoff to Java, once I have done all I need to with AWS CLI. > ? This is definitely the least painful, but it also means I don't use much Java at all. And any changes in upstream processing must be done in Bash to avoid handoff headaches from AWS CLI ---> Java. > ? Download the AWS SDK Jar files and just do it all in Java. > ? Ignoring the fact that some things are much harder to do via the AWS Java SDK's, there's actually some functionality that just isn't available via the Java ones. I'd have to recreate it myself, and it would be a significant lift. > Option 4 is best when I am building an application, but for ad-hoc checks that I want to do on the fly (my most common use-case by far), I have been using Option 3. > I just wish I could use more Java. It's a FAR BETTERtool than Bash, but I can't justify the level of effort for ad-hoc use cases because of the poor hand off from Java --> Bash. And since AWS CLI is only available via Bash/CMD, I'm stuck with a bunch of not-good choices. > CLI Scripting in Java is great, but I wanted to highlight this pain point to spread awareness. > Can you relate? > From cay.horstmann at gmail.com Mon Apr 14 19:06:07 2025 From: cay.horstmann at gmail.com (Cay Horstmann) Date: Mon, 14 Apr 2025 21:06:07 +0200 Subject: My experience using Java to do CLI Scripting In-Reply-To: <356624F4-6FE4-4D22-92EE-049C1E03472C@oracle.com> References: <356624F4-6FE4-4D22-92EE-049C1E03472C@oracle.com> Message-ID: <0be34f61-8b4b-494d-bb4a-2d21fb3127e7@gmail.com> Absolutely, ProcessBuilder/Process is the right approach. I realize that all those bells and whistles in the Process API are there for a reason, but the API is a bit clunky for the happy day path that one usually has in a script: running a process until it terminates and getting its output. It is trivial to write a couple of helper methods, but it might be nice if the Process API could help out. Something like Process p = Process.waitFor("ls", "-al"); String result = p.output(); Cheers, Cay PS. This isn't pretty in Python either: https://docs.python.org/3/library/subprocess.html#subprocess.run Il 12/04/25 17:02, Ron Pressler ha scritto: > Hi. > > Let?s focus on ProcessBuilder and Process (as I think that?s where you want to focus, and why I think this discussion is more appropriate for core-libs-dev). Can you try to show more concretely what the pain point is in your actual code? > > The ProcessBuilder example is long because it does multiple things, each of which may or may not be relevant to your use case. That doing five different things requires five lines of code doesn?t help us see where your specific pain point is. > > ? Ron > >> On 12 Apr 2025, at 15:39, David Alayachew wrote: >> >> Hello Amber Dev Team and Kulla Dev Team, >> >> (I made a reddit post too, if you prefer to interact there instead -- https://www.reddit.com/r/java/comments/1jx87ys/) >> The following JEP's have released recently. >> ? JEP 495: Simple Source Files and Instance Main Methods >> ? JEP 330: Launch Single-File Source-Code Programs >> ? JEP 222: jshell: The Java Shell (Read-Eval-Print Loop) >> These have made it really easy for me to do CLI scripting in Java, as opposed to Bash. However, I've run into some pain points, as I've relied more and more on Java. >> For starters, the hand off from Java --> Bash is kind of ugly. Bash --> Java is not bad, due to void main(final String[] args), as well as Bash's xargs. But Java --> Bash is ugly, and here is an example demonstrating how/why. >> I use AWS CLI to manage my dev environment. It's super powerful, and is all available directly from the CLI, using simple Bash or even CMD. >> Let's say I use AWS CLI to gather some ad-hoc information about my entire dev environment. How do I manage the multiple handoffs back and forth between AWS CLI and Java? >> There are no good answers. >> ? Store the results into a file, then use JShell/java(c) to process the output file from Bash/AWS CLI. >> ? There's multiple handoffs back and forth between AWS CLI and Java. So, every handoff from Java ---> AWS CLI means generating a new file, thus increasing the complexity and cruft. It's unideal. >> ? Use Java's ProcessBuilder and Process classes. >> ? This works, but is heavy-handed. Look at the examples in those links. That is multiple lines of code to represent a single bash command. It does appear to be the idiomatic way, though. >> ? Do all upstream processing with AWS CLI in Bash directly, then do only a single handoff to Java, once I have done all I need to with AWS CLI. >> ? This is definitely the least painful, but it also means I don't use much Java at all. And any changes in upstream processing must be done in Bash to avoid handoff headaches from AWS CLI ---> Java. >> ? Download the AWS SDK Jar files and just do it all in Java. >> ? Ignoring the fact that some things are much harder to do via the AWS Java SDK's, there's actually some functionality that just isn't available via the Java ones. I'd have to recreate it myself, and it would be a significant lift. >> Option 4 is best when I am building an application, but for ad-hoc checks that I want to do on the fly (my most common use-case by far), I have been using Option 3. >> I just wish I could use more Java. It's a FAR BETTERtool than Bash, but I can't justify the level of effort for ad-hoc use cases because of the poor hand off from Java --> Bash. And since AWS CLI is only available via Bash/CMD, I'm stuck with a bunch of not-good choices. >> CLI Scripting in Java is great, but I wanted to highlight this pain point to spread awareness. >> Can you relate? >> > -- Cay S. Horstmann | https://horstmann.com From mark.reinhold at oracle.com Mon Apr 14 20:02:27 2025 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Mon, 14 Apr 2025 20:02:27 +0000 Subject: New candidate JEP: 507: Primitive Types in Patterns, instanceof, and switch (Third Preview) Message-ID: <20250414200226.16F9A81187B@eggemoggin.niobe.net> https://openjdk.org/jeps/507 Summary: Enhance pattern matching by allowing primitive types in all pattern contexts, and extend instanceof and switch to work with all primitive types. This is a preview language feature. - Mark From ron.pressler at oracle.com Mon Apr 14 20:31:17 2025 From: ron.pressler at oracle.com (Ron Pressler) Date: Mon, 14 Apr 2025 20:31:17 +0000 Subject: My experience using Java to do CLI Scripting In-Reply-To: <0be34f61-8b4b-494d-bb4a-2d21fb3127e7@gmail.com> References: <356624F4-6FE4-4D22-92EE-049C1E03472C@oracle.com> <0be34f61-8b4b-494d-bb4a-2d21fb3127e7@gmail.com> Message-ID: <3A1C1BCE-FFAF-4D80-A447-D285DC7B141D@oracle.com> (discussion moved to core-libs-dev) > On 14 Apr 2025, at 20:06, Cay Horstmann wrote: > > Absolutely, ProcessBuilder/Process is the right approach. > > I realize that all those bells and whistles in the Process API are there for a reason, but the API is a bit clunky for the happy day path that one usually has in a script: running a process until it terminates and getting its output. It is trivial to write a couple of helper methods, but it might be nice if the Process API could help out. Something like > > Process p = Process.waitFor("ls", "-al"); > String result = p.output(); > > Cheers, > > Cay > > PS. This isn't pretty in Python either: https://docs.python.org/3/library/subprocess.html#subprocess.run > > > Il 12/04/25 17:02, Ron Pressler ha scritto: >> Hi. >> Let?s focus on ProcessBuilder and Process (as I think that?s where you want to focus, and why I think this discussion is more appropriate for core-libs-dev). Can you try to show more concretely what the pain point is in your actual code? >> The ProcessBuilder example is long because it does multiple things, each of which may or may not be relevant to your use case. That doing five different things requires five lines of code doesn?t help us see where your specific pain point is. >> ? Ron >>> On 12 Apr 2025, at 15:39, David Alayachew wrote: >>> >>> Hello Amber Dev Team and Kulla Dev Team, >>> >>> (I made a reddit post too, if you prefer to interact there instead -- https://www.reddit.com/r/java/comments/1jx87ys/) >>> The following JEP's have released recently. >>> ? JEP 495: Simple Source Files and Instance Main Methods >>> ? JEP 330: Launch Single-File Source-Code Programs >>> ? JEP 222: jshell: The Java Shell (Read-Eval-Print Loop) >>> These have made it really easy for me to do CLI scripting in Java, as opposed to Bash. However, I've run into some pain points, as I've relied more and more on Java. >>> For starters, the hand off from Java --> Bash is kind of ugly. Bash --> Java is not bad, due to void main(final String[] args), as well as Bash's xargs. But Java --> Bash is ugly, and here is an example demonstrating how/why. >>> I use AWS CLI to manage my dev environment. It's super powerful, and is all available directly from the CLI, using simple Bash or even CMD. >>> Let's say I use AWS CLI to gather some ad-hoc information about my entire dev environment. How do I manage the multiple handoffs back and forth between AWS CLI and Java? >>> There are no good answers. >>> ? Store the results into a file, then use JShell/java(c) to process the output file from Bash/AWS CLI. >>> ? There's multiple handoffs back and forth between AWS CLI and Java. So, every handoff from Java ---> AWS CLI means generating a new file, thus increasing the complexity and cruft. It's unideal. >>> ? Use Java's ProcessBuilder and Process classes. >>> ? This works, but is heavy-handed. Look at the examples in those links. That is multiple lines of code to represent a single bash command. It does appear to be the idiomatic way, though. >>> ? Do all upstream processing with AWS CLI in Bash directly, then do only a single handoff to Java, once I have done all I need to with AWS CLI. >>> ? This is definitely the least painful, but it also means I don't use much Java at all. And any changes in upstream processing must be done in Bash to avoid handoff headaches from AWS CLI ---> Java. >>> ? Download the AWS SDK Jar files and just do it all in Java. >>> ? Ignoring the fact that some things are much harder to do via the AWS Java SDK's, there's actually some functionality that just isn't available via the Java ones. I'd have to recreate it myself, and it would be a significant lift. >>> Option 4 is best when I am building an application, but for ad-hoc checks that I want to do on the fly (my most common use-case by far), I have been using Option 3. >>> I just wish I could use more Java. It's a FAR BETTERtool than Bash, but I can't justify the level of effort for ad-hoc use cases because of the poor hand off from Java --> Bash. And since AWS CLI is only available via Bash/CMD, I'm stuck with a bunch of not-good choices. >>> CLI Scripting in Java is great, but I wanted to highlight this pain point to spread awareness. >>> Can you relate? >>> > > -- > > Cay S. Horstmann | https://horstmann.com > From zjx001202 at gmail.com Mon Apr 14 20:54:07 2025 From: zjx001202 at gmail.com (Glavo) Date: Tue, 15 Apr 2025 04:54:07 +0800 Subject: New candidate JEP: 507: Primitive Types in Patterns, instanceof, and switch (Third Preview) In-Reply-To: <20250414200226.16F9A81187B@eggemoggin.niobe.net> References: <20250414200226.16F9A81187B@eggemoggin.niobe.net> Message-ID: Hi, It looks like it has not changed since the original version (JEP 488). Why does it need a third preview? This makes it not production-ready in JDK 25. Is there some hidden danger that I'm not aware of that makes people hesitant about it? Glavo On Tue, Apr 15, 2025 at 4:17?AM Mark Reinhold wrote: > https://openjdk.org/jeps/507 > > Summary: Enhance pattern matching by allowing primitive types in all > pattern contexts, and extend instanceof and switch to work with all > primitive types. This is a preview language feature. > > - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Tue Apr 15 09:39:50 2025 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 15 Apr 2025 09:39:50 +0000 Subject: New candidate JEP: 507: Primitive Types in Patterns, instanceof, and switch (Third Preview) In-Reply-To: References: <20250414200226.16F9A81187B@eggemoggin.niobe.net> Message-ID: Hi Glavo, There was some discussion about the reasons for previewing on the spec-experts list: https://mail.openjdk.org/pipermail/amber-spec-experts/2025-April/004254.html The basic point is that this JEP overlaps with forthcoming features in Valhalla; so we?re being cautious to make sure we don?t have to undo something that crops up from Valhalla. Kind request in the meantime: Please try this feature out in anger, and report back what you found (good and bad). We need more feedback from the community on preview features like this one :-) Thanks, Gavin On 14 Apr 2025, at 22:54, Glavo wrote: Hi, It looks like it has not changed since the original version (JEP 488). Why does it need a third preview? This makes it not production-ready in JDK 25. Is there some hidden danger that I'm not aware of that makes people hesitant about it? Glavo On Tue, Apr 15, 2025 at 4:17?AM Mark Reinhold > wrote: https://openjdk.org/jeps/507 Summary: Enhance pattern matching by allowing primitive types in all pattern contexts, and extend instanceof and switch to work with all primitive types. This is a preview language feature. - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From jean-noel.rouvignac at pingidentity.com Wed Apr 16 09:37:32 2025 From: jean-noel.rouvignac at pingidentity.com (=?UTF-8?Q?Jean=2DNo=C3=ABl_Rouvignac?=) Date: Wed, 16 Apr 2025 11:37:32 +0200 Subject: "Cannot resolve symbol al" after `else if (! (l instanceof ArrayList al))` Message-ID: Hello amber-dev folks! While using pattern matching for instanceof I noticed a small incoherent oddity. Take this code: ``` static void ensureCapacity(List l) { if (l instanceof LinkedList) { throw new IllegalArgumentException("Use an efficient list implementation"); } else if (!(l instanceof ArrayList al)) { return; } al.ensureCapacity(10); System.out.println("done"); } ensureCapacity(new ArrayList()); ``` The compiler rejects this code with: `Cannot resolve symbol 'al'`. (I have tested that code with JDK 24 in the playground ( https://dev.java/playground) but also in my IDE with JDK 21) However #1, removing the `else` keyword makes the code successfully compile: ``` } if (!(l instanceof ArrayList al)) { return; } ``` However #2, adding an additional `else` keyword makes the code successfully compile: ``` } else if (!(l instanceof ArrayList al)) { return; } else { al.ensureCapacity(10); System.out.println("done"); } ``` While option #1 and #2 are acceptable workarounds, it looks to me like there could be a bug in javac? Therefore I am bringing this to your attention so you can decide if it is a bug or not. I searched the JDK's bug tracker for this specific case, but could not find anything related. Thanks! Jean-No?l -- _CONFIDENTIALITY NOTICE: This email may contain confidential and privileged material for the sole use of the intended recipient(s). Any review, use, distribution or disclosure by others is strictly prohibited.? If you have received this communication in error, please notify the sender immediately by e-mail and delete the message and any file attachments from your computer. Thank you._ -------------- next part -------------- An HTML attachment was scrubbed... URL: From mark.reinhold at oracle.com Thu Apr 17 12:07:20 2025 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Thu, 17 Apr 2025 12:07:20 +0000 Subject: New candidate JEP: 511: Module Import Declarations Message-ID: <20250417120719.54FE3811E46@eggemoggin.niobe.net> https://openjdk.org/jeps/511 Summary: Enhance the Java programming language with the ability to succinctly import all of the packages exported by a module. This simplifies the reuse of modular libraries, but does not require the importing code to be in a module itself. - Mark From mark.reinhold at oracle.com Thu Apr 17 12:07:22 2025 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Thu, 17 Apr 2025 12:07:22 +0000 Subject: New candidate JEP: 512: Compact Source Files and Instance Main Methods Message-ID: <20250417120722.47AF8811E48@eggemoggin.niobe.net> https://openjdk.org/jeps/512 Summary: Evolve the Java programming language so that beginners can write their first programs without needing to understand language features designed for large programs. Far from using a separate dialect of the language, beginners can write streamlined declarations for single-class programs and then seamlessly expand their programs to use more advanced features as their skills grow. Experienced developers can likewise enjoy writing small programs succinctly, without the need for constructs intended for programming in the large. - Mark From andreas.brudin at gmail.com Sat Apr 19 16:17:43 2025 From: andreas.brudin at gmail.com (Andreas Berheim Brudin) Date: Sat, 19 Apr 2025 18:17:43 +0200 Subject: =?UTF-8?Q?Question_about_pattern_matching_and_sealed_types_=E2=80=93?= =?UTF-8?Q?_redundant_switch_cases?= Message-ID: Hi all, I'm new to the list?apologies if this has been discussed before, and thanks in advance for your time. I have a question about pattern matching with sealed types. Consider this example: sealed interface A permits B, C {} record B(String b1, String b2) implements A {} record C(String c) implements A {} A myVar = ...; if (!(myVar instanceof B(var b1, var b2))) { return switch (myVar) { case C(var c) -> c; case B b -> throw new IllegalStateException("should not happen"); }; } // use b1, b2 Here, I want to keep an early-return style, but since I need to return a value from C, I have to use a switch. This leads to redundancy: we've already tested myVar is not a B, so that case should be statically unreachable. My questions: 1. Is there any consideration or ongoing work to allow the compiler to automatically eliminate such unreachable cases? 2. If only one case remains (in this case, C), could it be possible to treat the variable as a C directly without requiring an explicit switch? Again, apologies if this has already been discussed. I'd appreciate any pointers to relevant threads or JEPs if so. Thanks, Andreas -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Apr 19 16:59:57 2025 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 19 Apr 2025 12:59:57 -0400 Subject: =?UTF-8?Q?Re=3A_Question_about_pattern_matching_and_sealed_types_?= =?UTF-8?Q?=E2=80=93_redundant_switch_cases?= In-Reply-To: References: Message-ID: This doesn't really have as much to do with sealed types or pattern matching as it might seem; it has to do with to what degree the language is willing to engage in flow-based type analysis. The short answer is: there's not a lot of appetite for investing in this.? The cost and complexity is likely high both initially and ongoing, and the impact on program correctness is fairly marginal.? (The simple cases are easy, but just handling the simple cases is a recipe for an infinite stream of enhancement requests.) If we're going to invest at all in flow-based type analysis, we'd likely start with nullity analysis.? This is a simpler problem (and still, not simple) and probably would have more impact. On 4/19/2025 12:17 PM, Andreas Berheim Brudin wrote: > Hi all, > > I'm new to the list?apologies if this has been discussed before, and > thanks in advance for your time. > > I have a question about pattern matching with sealed types. Consider > this example: > > sealed interface A permits B, C {} > record B(String b1, String b2) implements A {} > record C(String c) implements A {} > > A myVar = ...; > if (!(myVar instanceof B(var b1, var b2))) { > ? ? return switch (myVar) { > ? ? ? ? case C(var c) -> c; > ? ? ? ? case B b -> throw new IllegalStateException("should not happen"); > ? ? }; > } > // use b1, b2 > > Here, I want to keep an early-return style, but since I need to return > a value from C, I have to use a switch. This leads to redundancy: > we've already tested myVar is not a B, so that case should be > statically unreachable. > > My questions: > > 1. Is there any consideration or ongoing work to allow the compiler to > automatically eliminate such unreachable cases? > > 2. If only one case remains (in this case, C), could it be possible to > treat the variable as a C directly without requiring an explicit switch? > > Again, apologies if this has already been discussed. I'd appreciate > any pointers to relevant threads or JEPs if so. > > Thanks, > Andreas -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Apr 19 17:08:03 2025 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 19 Apr 2025 19:08:03 +0200 (CEST) Subject: =?utf-8?Q?Re:_Question_about_pattern_matching_an?= =?utf-8?Q?d_sealed_types_=E2=80=93_redundant_switch_cases?= In-Reply-To: References: Message-ID: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> > From: "Andreas Berheim Brudin" > To: "amber-dev" > Sent: Saturday, April 19, 2025 6:17:43 PM > Subject: Question about pattern matching and sealed types ? redundant switch > cases > Hi all, > I'm new to the list?apologies if this has been discussed before, and thanks in > advance for your time. > I have a question about pattern matching with sealed types. Consider this > example: > sealed interface A permits B, C {} > record B(String b1, String b2) implements A {} > record C(String c) implements A {} > A myVar = ...; > if (!(myVar instanceof B(var b1, var b2))) { > return switch (myVar) { > case C(var c) -> c; > case B b -> throw new IllegalStateException("should not happen"); > }; > } > // use b1, b2 > Here, I want to keep an early-return style, but since I need to return a value > from C, I have to use a switch. This leads to redundancy: we've already tested > myVar is not a B, so that case should be statically unreachable. > My questions: > 1. Is there any consideration or ongoing work to allow the compiler to > automatically eliminate such unreachable cases? The short answer is no :) Long answer, (1) myVar does not change its type, it is declared as an A, it always be an A (Groovy or Kotlin behave differently, they use flow typing) (2) the type system of Java has no notion of exclusion, the type A but not B does not exist (3) instanceof and switch does not have the same semantics, instanceof has no notion of exhaustiveness while a switch on a sealed type has. so because of (1) the type of myVar can not be changed, because of (2) its new type inside the if can not be "A but not B" and because of (3) the new type can not be only C. > 2. If only one case remains (in this case, C), could it be possible to treat the > variable as a C directly without requiring an explicit switch? No, > Again, apologies if this has already been discussed. I'd appreciate any pointers > to relevant threads or JEPs if so. The relevant JEPs are JEP 394 (for instanceof) and 441 (for switch). > Thanks, > Andreas regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Apr 19 17:17:18 2025 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 19 Apr 2025 19:17:18 +0200 (CEST) Subject: =?utf-8?Q?Re:_Question_about_pattern_matching_an?= =?utf-8?Q?d_sealed_types_=E2=80=93_redundant_switch_cases?= In-Reply-To: References: Message-ID: <1389735668.235396227.1745083038814.JavaMail.zimbra@univ-eiffel.fr> > From: "Brian Goetz" > To: "Andreas Berheim Brudin" , "amber-dev" > > Sent: Saturday, April 19, 2025 6:59:57 PM > Subject: Re: Question about pattern matching and sealed types ? redundant switch > cases > This doesn't really have as much to do with sealed types or pattern matching as > it might seem; it has to do with to what degree the language is willing to > engage in flow-based type analysis. > The short answer is: there's not a lot of appetite for investing in this. The > cost and complexity is likely high both initially and ongoing, and the impact > on program correctness is fairly marginal. (The simple cases are easy, but just > handling the simple cases is a recipe for an infinite stream of enhancement > requests.) > If we're going to invest at all in flow-based type analysis, we'd likely start > with nullity analysis. This is a simpler problem (and still, not simple) and > probably would have more impact. This is also because we may restrict nullity analysis to only a type-checking pass that has no impact on the generated code while general flow typing change the generated code by changing method selection, inserting casts or instanceof + throw so adding such analysis after the fact is more disruptive. regards, R?mi > On 4/19/2025 12:17 PM, Andreas Berheim Brudin wrote: >> Hi all, >> I'm new to the list?apologies if this has been discussed before, and thanks in >> advance for your time. >> I have a question about pattern matching with sealed types. Consider this >> example: >> sealed interface A permits B, C {} >> record B(String b1, String b2) implements A {} >> record C(String c) implements A {} >> A myVar = ...; >> if (!(myVar instanceof B(var b1, var b2))) { >> return switch (myVar) { >> case C(var c) -> c; >> case B b -> throw new IllegalStateException("should not happen"); >> }; >> } >> // use b1, b2 >> Here, I want to keep an early-return style, but since I need to return a value >> from C, I have to use a switch. This leads to redundancy: we've already tested >> myVar is not a B, so that case should be statically unreachable. >> My questions: >> 1. Is there any consideration or ongoing work to allow the compiler to >> automatically eliminate such unreachable cases? >> 2. If only one case remains (in this case, C), could it be possible to treat the >> variable as a C directly without requiring an explicit switch? >> Again, apologies if this has already been discussed. I'd appreciate any pointers >> to relevant threads or JEPs if so. >> Thanks, >> Andreas -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Apr 19 17:19:30 2025 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 19 Apr 2025 13:19:30 -0400 Subject: =?UTF-8?Q?Re=3A_Question_about_pattern_matching_and_sealed_types_?= =?UTF-8?Q?=E2=80=93_redundant_switch_cases?= In-Reply-To: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> References: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> Message-ID: It's not as bad as you say, but the answer is still the same :) It is possible to do flow-specific refinement without actually changing how we track the type.? So it would be possible to say that the type of myVar is still A, while maintaining a lattice of type inclusion/exclusion refinements that would not actually change semantics (such as method overload selection), but could be used to detect unreachable code. But the return on that effort seems pretty squarely in the "there are much better things we could work on" bucket. On 4/19/2025 1:08 PM, Remi Forax wrote: > > > ------------------------------------------------------------------------ > > *From: *"Andreas Berheim Brudin" > *To: *"amber-dev" > *Sent: *Saturday, April 19, 2025 6:17:43 PM > *Subject: *Question about pattern matching and sealed types ? > redundant switch cases > > Hi all, > > I'm new to the list?apologies if this has been discussed before, > and thanks in advance for your time. > > I have a question about pattern matching with sealed types. > Consider this example: > > sealed interface A permits B, C {} > record B(String b1, String b2) implements A {} > record C(String c) implements A {} > > A myVar = ...; > if (!(myVar instanceof B(var b1, var b2))) { > ? ? return switch (myVar) { > ? ? ? ? case C(var c) -> c; > ? ? ? ? case B b -> throw new IllegalStateException("should not > happen"); > ? ? }; > } > // use b1, b2 > > Here, I want to keep an early-return style, but since I need to > return a value from C, I have to use a switch. This leads to > redundancy: we've already tested myVar is not a B, so that case > should be statically unreachable. > > My questions: > > 1. Is there any consideration or ongoing work to allow the > compiler to automatically eliminate such unreachable cases? > > > The short answer is no :) > > Long answer, > ? (1) myVar does not change its type, it is declared as an A, it > always be an A (Groovy or Kotlin behave differently, they use flow typing) > ? (2) the type system of Java has no notion of exclusion, the type A > but not B does not exist > ? (3) instanceof and switch does not have the same semantics, > instanceof has no notion of exhaustiveness while a switch on a sealed > type has. > > so because of (1) the type of myVar can not be changed, because of (2) > its new type inside the if can not be "A but not B" and because of (3) > the new type can not be only C. > > > > 2. If only one case remains (in this case, C), could it be > possible to treat the variable as a C directly without requiring > an explicit switch? > > > No, > > > > Again, apologies if this has already been discussed. I'd > appreciate any pointers to relevant threads or JEPs if so. > > > The relevant JEPs are JEP 394 (for instanceof) and 441 (for switch). > > > > Thanks, > Andreas > > > regards, > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From andreas.brudin at gmail.com Sat Apr 19 17:26:08 2025 From: andreas.brudin at gmail.com (Andreas Berheim Brudin) Date: Sat, 19 Apr 2025 19:26:08 +0200 Subject: =?UTF-8?Q?Re=3A_Question_about_pattern_matching_and_sealed_types?= =?UTF-8?Q?_=E2=80=93_redundant_switch_cases?= In-Reply-To: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> References: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Thank you both for the quick replies. I guess the fact that b1 and b2 is available outside of the if, hints at some kind of flow-based type analysis (even though the new type is implicitly declared in the pattern), but I would then have proven Brian's point - that once that door is open, people will ask for more. But given the semantics of Java, I understand that this doesn't fit very well. The reason I ask was that we use a Result type (Ok/Err) quite a lot, and the biggest complaint is the nestedness of the code, which often becomes: return switch(getSomething()) { case Err(var e) -> yield someError(e); case Ok(var something) -> { yield switch (doThingWithSomething(something)) { case Err(var e) -> yield someOtherError(e); case Ok(var result) -> etc... } } I guess you don't have any quick fixes for that other than resorting to traditional exception handling. Best, Andreas On Sat, Apr 19, 2025 at 7:08?PM Remi Forax wrote: > > > ------------------------------ > > *From: *"Andreas Berheim Brudin" > *To: *"amber-dev" > *Sent: *Saturday, April 19, 2025 6:17:43 PM > *Subject: *Question about pattern matching and sealed types ? redundant > switch cases > > Hi all, > > I'm new to the list?apologies if this has been discussed before, and > thanks in advance for your time. > > I have a question about pattern matching with sealed types. Consider this > example: > > sealed interface A permits B, C {} > record B(String b1, String b2) implements A {} > record C(String c) implements A {} > > A myVar = ...; > if (!(myVar instanceof B(var b1, var b2))) { > return switch (myVar) { > case C(var c) -> c; > case B b -> throw new IllegalStateException("should not happen"); > }; > } > // use b1, b2 > > Here, I want to keep an early-return style, but since I need to return a > value from C, I have to use a switch. This leads to redundancy: we've > already tested myVar is not a B, so that case should be statically > unreachable. > > My questions: > > 1. Is there any consideration or ongoing work to allow the compiler to > automatically eliminate such unreachable cases? > > > The short answer is no :) > > Long answer, > (1) myVar does not change its type, it is declared as an A, it always be > an A (Groovy or Kotlin behave differently, they use flow typing) > (2) the type system of Java has no notion of exclusion, the type A but > not B does not exist > (3) instanceof and switch does not have the same semantics, instanceof > has no notion of exhaustiveness while a switch on a sealed type has. > > so because of (1) the type of myVar can not be changed, because of (2) its > new type inside the if can not be "A but not B" and because of (3) the new > type can not be only C. > > > > 2. If only one case remains (in this case, C), could it be possible to > treat the variable as a C directly without requiring an explicit switch? > > > No, > > > > Again, apologies if this has already been discussed. I'd appreciate any > pointers to relevant threads or JEPs if so. > > > The relevant JEPs are JEP 394 (for instanceof) and 441 (for switch). > > > > Thanks, > Andreas > > > regards, > R?mi > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Apr 19 17:34:14 2025 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 19 Apr 2025 13:34:14 -0400 Subject: =?UTF-8?Q?Re=3A_Question_about_pattern_matching_and_sealed_types_?= =?UTF-8?Q?=E2=80=93_redundant_switch_cases?= In-Reply-To: References: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Right, you've built a monad here, and you want to express the happy path with a monadic do.? In Haskell you'd write this as: ??? do ??????? something <- getSomething() ??????? result <- doThingWithSomething(something) and let the language desugar it accordingly.? But you can do this yourself in Java! ??? sealed interface Result permits Ok, Err { ??????? Result andThen(ThrowyFunction f); ??????? static Result of(ThrowySupplier s) { ... } ??? } and then express your code as: ??? Result.of(() -> getSomething()) ????????? .andThen(C::doThingWithSomething) ????????? ... No nested code needed. On 4/19/2025 1:26 PM, Andreas Berheim Brudin wrote: > Thank you both for the quick replies. > > I guess the fact that b1 and b2 is available outside of the if, hints > at some kind of flow-based type analysis (even though the new type is > implicitly declared in the pattern), but I would then have proven > Brian's point - that once that door is open, people will ask for more. > > But given the semantics of Java, I understand that this doesn't fit > very well. The reason I ask was that we use a Result type (Ok/Err) > quite a lot, and the biggest complaint is the nestedness of the code, > which often becomes: > return switch(getSomething()) { > ? case Err(var e) -> yield someError(e); > ? case Ok(var something) -> { > ??? yield switch (doThingWithSomething(something)) { > ????? case Err(var e) -> yield someOtherError(e); > ????? case Ok(var result) -> etc... > ? } > } > > I guess you don't have any quick fixes for that other than resorting > to traditional exception handling. > > Best, > Andreas > > On Sat, Apr 19, 2025 at 7:08?PM Remi Forax wrote: > > > > ------------------------------------------------------------------------ > > *From: *"Andreas Berheim Brudin" > *To: *"amber-dev" > *Sent: *Saturday, April 19, 2025 6:17:43 PM > *Subject: *Question about pattern matching and sealed types ? > redundant switch cases > > Hi all, > > I'm new to the list?apologies if this has been discussed > before, and thanks in advance for your time. > > I have a question about pattern matching with sealed types. > Consider this example: > > sealed interface A permits B, C {} > record B(String b1, String b2) implements A {} > record C(String c) implements A {} > > A myVar = ...; > if (!(myVar instanceof B(var b1, var b2))) { > ? ? return switch (myVar) { > ? ? ? ? case C(var c) -> c; > ? ? ? ? case B b -> throw new IllegalStateException("should > not happen"); > ? ? }; > } > // use b1, b2 > > Here, I want to keep an early-return style, but since I need > to return a value from C, I have to use a switch. This leads > to redundancy: we've already tested myVar is not a B, so that > case should be statically unreachable. > > My questions: > > 1. Is there any consideration or ongoing work to allow the > compiler to automatically eliminate such unreachable cases? > > > The short answer is no :) > > Long answer, > ? (1) myVar does not change its type, it is declared as an A, it > always be an A (Groovy or Kotlin behave differently, they use flow > typing) > ? (2) the type system of Java has no notion of exclusion, the type > A but not B does not exist > ? (3) instanceof and switch does not have the same semantics, > instanceof has no notion of exhaustiveness while a switch on a > sealed type has. > > so because of (1) the type of myVar can not be changed, because of > (2) its new type inside the if can not be "A but not B" and > because of (3) the new type can not be only C. > > > > 2. If only one case remains (in this case, C), could it be > possible to treat the variable as a C directly without > requiring an explicit switch? > > > No, > > > > Again, apologies if this has already been discussed. I'd > appreciate any pointers to relevant threads or JEPs if so. > > > The relevant JEPs are JEP 394 (for instanceof) and 441 (for switch). > > > > Thanks, > Andreas > > > regards, > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From andreas.brudin at gmail.com Sat Apr 19 17:42:55 2025 From: andreas.brudin at gmail.com (Andreas Berheim Brudin) Date: Sat, 19 Apr 2025 19:42:55 +0200 Subject: =?UTF-8?Q?Re=3A_Question_about_pattern_matching_and_sealed_types?= =?UTF-8?Q?_=E2=80=93_redundant_switch_cases?= In-Reply-To: References: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> Message-ID: True, you may very well have a point here. I actually have the "andThen" method on my Result, but I call it "flatMap" for consistency. Some people had a hard time understanding what was going on and I don't want to be the one pushing for every developer needing a degree in category theory so I am trying to introduce this safe programming style without requiring familiarity with too advanced functional concepts (even though many seem happy to flatMap their lists!). But I guess just calling it "andThen" might alleviate some of the burden, thanks! /Andreas On Sat, Apr 19, 2025 at 7:34?PM Brian Goetz wrote: > Right, you've built a monad here, and you want to express the happy path > with a monadic do. In Haskell you'd write this as: > > do > something <- getSomething() > result <- doThingWithSomething(something) > > and let the language desugar it accordingly. But you can do this yourself > in Java! > > sealed interface Result permits Ok, Err { > Result andThen(ThrowyFunction f); > static Result of(ThrowySupplier s) { ... } > } > > and then express your code as: > > Result.of(() -> getSomething()) > .andThen(C::doThingWithSomething) > ... > > No nested code needed. > > > On 4/19/2025 1:26 PM, Andreas Berheim Brudin wrote: > > Thank you both for the quick replies. > > I guess the fact that b1 and b2 is available outside of the if, hints at > some kind of flow-based type analysis (even though the new type is > implicitly declared in the pattern), but I would then have proven Brian's > point - that once that door is open, people will ask for more. > > But given the semantics of Java, I understand that this doesn't fit very > well. The reason I ask was that we use a Result type (Ok/Err) quite a lot, > and the biggest complaint is the nestedness of the code, which often > becomes: > return switch(getSomething()) { > case Err(var e) -> yield someError(e); > case Ok(var something) -> { > yield switch (doThingWithSomething(something)) { > case Err(var e) -> yield someOtherError(e); > case Ok(var result) -> etc... > } > } > > I guess you don't have any quick fixes for that other than resorting to > traditional exception handling. > > Best, > Andreas > > On Sat, Apr 19, 2025 at 7:08?PM Remi Forax wrote: > >> >> >> ------------------------------ >> >> *From: *"Andreas Berheim Brudin" >> *To: *"amber-dev" >> *Sent: *Saturday, April 19, 2025 6:17:43 PM >> *Subject: *Question about pattern matching and sealed types ? redundant >> switch cases >> >> Hi all, >> >> I'm new to the list?apologies if this has been discussed before, and >> thanks in advance for your time. >> >> I have a question about pattern matching with sealed types. Consider this >> example: >> >> sealed interface A permits B, C {} >> record B(String b1, String b2) implements A {} >> record C(String c) implements A {} >> >> A myVar = ...; >> if (!(myVar instanceof B(var b1, var b2))) { >> return switch (myVar) { >> case C(var c) -> c; >> case B b -> throw new IllegalStateException("should not happen"); >> }; >> } >> // use b1, b2 >> >> Here, I want to keep an early-return style, but since I need to return a >> value from C, I have to use a switch. This leads to redundancy: we've >> already tested myVar is not a B, so that case should be statically >> unreachable. >> >> My questions: >> >> 1. Is there any consideration or ongoing work to allow the compiler to >> automatically eliminate such unreachable cases? >> >> >> The short answer is no :) >> >> Long answer, >> (1) myVar does not change its type, it is declared as an A, it always >> be an A (Groovy or Kotlin behave differently, they use flow typing) >> (2) the type system of Java has no notion of exclusion, the type A but >> not B does not exist >> (3) instanceof and switch does not have the same semantics, instanceof >> has no notion of exhaustiveness while a switch on a sealed type has. >> >> so because of (1) the type of myVar can not be changed, because of (2) >> its new type inside the if can not be "A but not B" and because of (3) the >> new type can not be only C. >> >> >> >> 2. If only one case remains (in this case, C), could it be possible to >> treat the variable as a C directly without requiring an explicit switch? >> >> >> No, >> >> >> >> Again, apologies if this has already been discussed. I'd appreciate any >> pointers to relevant threads or JEPs if so. >> >> >> The relevant JEPs are JEP 394 (for instanceof) and 441 (for switch). >> >> >> >> Thanks, >> Andreas >> >> >> regards, >> R?mi >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Apr 19 17:51:59 2025 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 19 Apr 2025 13:51:59 -0400 Subject: =?UTF-8?Q?Re=3A_Question_about_pattern_matching_and_sealed_types_?= =?UTF-8?Q?=E2=80=93_redundant_switch_cases?= In-Reply-To: References: <1920807543.235395784.1745082483075.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Yes, `flatMap` would be the traditional name.? I used `andThen` because `flatMap` scares a lot of Java developers.? And it's fine for you to do the same!? I think you would agree that it's better to get your team writing the simpler, more readable code, than being scared off by the "correct" terminology.? Some day they will realize that they've been flatMapping for years, and it's not as scary as they thought. If I were designing this API I would also want to do a requirements analysis on error handling modes.? In your original example, you didn't just throw whatever error the Err wrapped; you wrapped that error with some other error, that presumably adds some context to the error, like what you were trying to do at the time.? And that can be pretty valuable for diagnosing the app when things go wrong.? So you may want to provide some support for mapping errors as well, to widen the happy path that you want to keep developers on. On 4/19/2025 1:42 PM, Andreas Berheim Brudin wrote: > True, you may very well have a point here. > > I actually have the "andThen" method on my Result, but I call it > "flatMap" for consistency. Some people had a hard time understanding > what was going on and I don't want to be the one pushing for every > developer needing a degree in category theory so I am trying to > introduce this safe programming style without requiring familiarity > with too advanced functional concepts (even though many seem happy to > flatMap their lists!). > > But I guess just calling it "andThen" might alleviate some of the > burden, thanks! > > /Andreas > > On Sat, Apr 19, 2025 at 7:34?PM Brian Goetz > wrote: > > Right, you've built a monad here, and you want to express the > happy path with a monadic do.? In Haskell you'd write this as: > > ??? do > ??????? something <- getSomething() > ??????? result <- doThingWithSomething(something) > > and let the language desugar it accordingly.? But you can do this > yourself in Java! > > ??? sealed interface Result permits Ok, Err { > ??????? Result andThen(ThrowyFunction f); > ??????? static Result of(ThrowySupplier s) { ... } > ??? } > > and then express your code as: > > ??? Result.of(() -> getSomething()) > ????????? .andThen(C::doThingWithSomething) > ????????? ... > > No nested code needed. > > > On 4/19/2025 1:26 PM, Andreas Berheim Brudin wrote: >> Thank you both for the quick replies. >> >> I guess the fact that b1 and b2 is available outside of the if, >> hints at some kind of flow-based type analysis (even though the >> new type is implicitly declared in the pattern), but I would then >> have proven Brian's point - that once that door is open, people >> will ask for more. >> >> But given the semantics of Java, I understand that this doesn't >> fit very well. The reason I ask was that we use a Result type >> (Ok/Err) quite a lot, and the biggest complaint is the nestedness >> of the code, which often becomes: >> return switch(getSomething()) { >> ? case Err(var e) -> yield someError(e); >> ? case Ok(var something) -> { >> ??? yield switch (doThingWithSomething(something)) { >> ????? case Err(var e) -> yield someOtherError(e); >> ????? case Ok(var result) -> etc... >> ? } >> } >> >> I guess you don't have any quick fixes for that other than >> resorting to traditional exception handling. >> >> Best, >> Andreas >> >> On Sat, Apr 19, 2025 at 7:08?PM Remi Forax wrote: >> >> >> >> ------------------------------------------------------------------------ >> >> *From: *"Andreas Berheim Brudin" >> *To: *"amber-dev" >> *Sent: *Saturday, April 19, 2025 6:17:43 PM >> *Subject: *Question about pattern matching and sealed >> types ? redundant switch cases >> >> Hi all, >> >> I'm new to the list?apologies if this has been discussed >> before, and thanks in advance for your time. >> >> I have a question about pattern matching with sealed >> types. Consider this example: >> >> sealed interface A permits B, C {} >> record B(String b1, String b2) implements A {} >> record C(String c) implements A {} >> >> A myVar = ...; >> if (!(myVar instanceof B(var b1, var b2))) { >> ? ? return switch (myVar) { >> ? ? ? ? case C(var c) -> c; >> ? ? ? ? case B b -> throw new >> IllegalStateException("should not happen"); >> ? ? }; >> } >> // use b1, b2 >> >> Here, I want to keep an early-return style, but since I >> need to return a value from C, I have to use a switch. >> This leads to redundancy: we've already tested myVar is >> not a B, so that case should be statically unreachable. >> >> My questions: >> >> 1. Is there any consideration or ongoing work to allow >> the compiler to automatically eliminate such unreachable >> cases? >> >> >> The short answer is no :) >> >> Long answer, >> ? (1) myVar does not change its type, it is declared as an A, >> it always be an A (Groovy or Kotlin behave differently, they >> use flow typing) >> ? (2) the type system of Java has no notion of exclusion, the >> type A but not B does not exist >> ? (3) instanceof and switch does not have the same semantics, >> instanceof has no notion of exhaustiveness while a switch on >> a sealed type has. >> >> so because of (1) the type of myVar can not be changed, >> because of (2) its new type inside the if can not be "A but >> not B" and because of (3) the new type can not be only C. >> >> >> >> 2. If only one case remains (in this case, C), could it >> be possible to treat the variable as a C directly without >> requiring an explicit switch? >> >> >> No, >> >> >> >> Again, apologies if this has already been discussed. I'd >> appreciate any pointers to relevant threads or JEPs if so. >> >> >> The relevant JEPs are JEP 394 (for instanceof) and 441 (for >> switch). >> >> >> >> Thanks, >> Andreas >> >> >> regards, >> R?mi >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: