From david.1993grajales at gmail.com Sun May 4 07:35:34 2025 From: david.1993grajales at gmail.com (david Grajales) Date: Sun, 4 May 2025 02:35:34 -0500 Subject: Suggestion: A "record"-like Construct to Model Stateless Behavior in Java Message-ID: Hi Amber Team. The following is not "a solution" it's more like an idea or suggestion to illustrate the core idea and intention based on experience, the core idea is "java should have a convenient construct that is the equivalent of records but for modeling behavior only. so please take the following just as a mean for explanation. The more I use records and learn about DOP (Data-Oriented Programming), the more I find myself using utility classes to separate data modeling from behavior, and to enforce immutability by creating both immutable data structures and stateless static methods that manage the data. For example, let's say we have a User record whose personal data is retrieved from a database or any other entry point, with some data encrypted in a details field. The utility class to decrypt and obtain the UserDetails for business logic would look like this: public record User( String email, String password, String encryptedDetails ) {} public record UserDetails( String phone, String idDocument, String name ) {} public final class EncryptionUtilityClass { private static final String key; public static final String value = "foo"; private static final Gson gson = new Gson; // or any other deserialization library static { key = getConfigurationKey(); } private EncryptionUtilityClass() {} private static String getConfigurationKey() { return "keyFromConfigFile"; } public static UserDetails decryptUserDetails(String details) { String data = EncryptionLibraryClass.decrypt(details, key); return gson.fromJson(data, UserDetails.class); } public static String encryptUserDetails(UserDetails userDetails) { return EncryptionLibraryClass.encrypt(gson.toJson(userDetails), key); } } This is a very common pattern to create utility classes that are meant to contain only constant values and static methods (such as the Math class in Java SE): 1. Define a final class. 2. Declare a private constructor to prevent instantiation. 3. Expose only static methods and optionally static final constants (possibly initialized in a static block). This pattern is verbose and requires the programmer to be aware of it as it lacks the semantics to clearly express the class?s intention: to serve only as a namespace for stateless functions. Much like how record was introduced to model immutable data more expressively and concisely, it would be valuable to introduce a dedicated construct that semantically declares a class as a stateless, behavioral container?a "function-only" namespace, with no instances or mutable state. Such a construct could implicitly provide: - The class being final - All methods being static - Only one constructor, implicitly private - All fields being static final, requiring initialization via assignment or static blocks - Disallowance of instance-level fields or methods This would improve not only readability and clarity of intent but could also enable the compiler and JVM to optimize the generated code more directly by emitting invokestatic calls directly, avoiding later optimization passes, and supporting aggressive inlining and constant folding where possible. A keyword or construct name could be namespace, as it aligns with the idea of a purely functions and constants grouping. Alternatives might include utility class or allowing outer static classes. Here is how an hypothetical namespace construct could look like: public namespace EncryptionUtilities { // alternative public utility class. No need for final modifier private String key; //No need for static final fields, implicitly assumed as such public String value = "foo"; // since fields are strictly initialized and no instances are allowed one can pass public fields directly and assume these will always be in a correct state private Gson gson = new Gson; static { key = getConfigurationKey(); } // No private constructor requried or even allowed private String getConfigurationKey() { // No need for static modifier on methods either public or private return "keyFromConfigFile"; } UserDetails decryptUserDetails(String details) { String data = EncryptionLibraryClass.decrypt(details, key); return gson.fromJson(data, UserDetails.class); } String encryptUserDetails(UserDetails userDetails) { return EncryptionLibraryClass.encrypt(gson.toJson(userDetails), key); } } Such feature would benefit a wide range of use cases?helper libraries (encryption, validation, logging, etc), stateless DSLs, functional-style code, immutable-style APIs, etc.?not only for conciseness, but more importantly, for providing clearer semantics to both developers about intention and the JVM for enable optimization at compilation time. Thank you for your continued work on improving the Java language. I have been using records and DOP in both my personal projects and prod; this suggestion is heavily inspired by this experience. I hope this insight helps. Best regards and always yours. -------------- next part -------------- An HTML attachment was scrubbed... URL: From nickhensel25 at icloud.com Tue May 6 18:31:30 2025 From: nickhensel25 at icloud.com (Nick Hensel) Date: Tue, 6 May 2025 18:31:30 +0000 (UTC) Subject: Current State of "withers" (derived record creation) Message-ID: <4fdecf92-e6ae-4130-94a4-5feafa83147f@me.com> Hi Amber Team. I'm currently checking out what could be finalized in java 25 and noticed that the "Derived record creation" JEP ( https://openjdk.org/jeps/468 ) hasn't been updated since last year. Is there any information available on the current state of this JEP or is it likely to be worked on in the near future? The feature would be very convenient in many cases. Best regards, Nick Hensel -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Wed May 7 13:37:06 2025 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Wed, 7 May 2025 13:37:06 +0000 Subject: Current State of "withers" (derived record creation) In-Reply-To: <4fdecf92-e6ae-4130-94a4-5feafa83147f@me.com> References: <4fdecf92-e6ae-4130-94a4-5feafa83147f@me.com> Message-ID: Hello! Derived record creation fits in the biggest picture of composing construction and deconstruction together (new class instance creation and pattern matching). While the basic building blocks are there for record classes, we are making progress on the deconstruction of normal classes as well. When the picture is clearer there, we can circle back to withers once again, since it is still on our list of priorities. Best, Angelos ________________________________ From: amber-dev on behalf of Nick Hensel Sent: 06 May 2025 20:31 To: amber-dev at openjdk.org Subject: Current State of "withers" (derived record creation) Hi Amber Team. I'm currently checking out what could be finalized in java 25 and noticed that the "Derived record creation" JEP (https://openjdk.org/jeps/468) hasn't been updated since last year. Is there any information available on the current state of this JEP or is it likely to be worked on in the near future? The feature would be very convenient in many cases. Best regards, Nick Hensel -------------- next part -------------- An HTML attachment was scrubbed... URL: From nickhensel25 at icloud.com Thu May 8 18:42:05 2025 From: nickhensel25 at icloud.com (Nick Hensel) Date: Thu, 8 May 2025 20:42:05 +0200 Subject: Current State of "withers" (derived record creation) In-Reply-To: References: Message-ID: <27AD1773-8285-44D8-8C51-0B0984CAD682@icloud.com> Thanks for the quick answer. Is there any place, where the ongoing development of those new features can be observed beside this mailing list? Best, Nick > Am 07.05.2025 um 15:37 schrieb Angelos Bimpoudis : > > ? > Hello! > > Derived record creation fits in the biggest picture of composing construction > and deconstruction together (new class instance creation and pattern matching). > While the basic building blocks are there for record classes, we are making > progress on the deconstruction of normal classes as well. When the picture is > clearer there, we can circle back to withers once again, since it is still on > our list of priorities. > > Best, > Angelos > From: amber-dev on behalf of Nick Hensel > Sent: 06 May 2025 20:31 > To: amber-dev at openjdk.org > Subject: Current State of "withers" (derived record creation) > > Hi Amber Team. > > I'm currently checking out what could be finalized in java 25 and noticed that the "Derived record creation" JEP (https://openjdk.org/jeps/468) hasn't been updated since last year. > > Is there any information available on the current state of this JEP or is it likely to be worked on in the near future? The feature would be very convenient in many cases. > > Best regards, > Nick Hensel > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Thu May 8 20:58:05 2025 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 8 May 2025 20:58:05 +0000 Subject: Current State of "withers" (derived record creation) In-Reply-To: <27AD1773-8285-44D8-8C51-0B0984CAD682@icloud.com> References: <27AD1773-8285-44D8-8C51-0B0984CAD682@icloud.com> Message-ID: Hi Nick, We also discuss design issues on the amber spec experts list. We have not discussed this feature for a while though, for the reasons that Angelos listed. (Summary: We want to offer withers for all classes, so we need to get that story clear in our minds so that we don?t make a decision for record classes that could get in the way.) Cheers, Gavin On 8 May 2025, at 19:42, Nick Hensel wrote: Thanks for the quick answer. Is there any place, where the ongoing development of those new features can be observed beside this mailing list? Best, Nick Am 07.05.2025 um 15:37 schrieb Angelos Bimpoudis : ? Hello! Derived record creation fits in the biggest picture of composing construction and deconstruction together (new class instance creation and pattern matching). While the basic building blocks are there for record classes, we are making progress on the deconstruction of normal classes as well. When the picture is clearer there, we can circle back to withers once again, since it is still on our list of priorities. Best, Angelos ________________________________ From: amber-dev on behalf of Nick Hensel Sent: 06 May 2025 20:31 To: amber-dev at openjdk.org Subject: Current State of "withers" (derived record creation) Hi Amber Team. I'm currently checking out what could be finalized in java 25 and noticed that the "Derived record creation" JEP (https://openjdk.org/jeps/468) hasn't been updated since last year. Is there any information available on the current state of this JEP or is it likely to be worked on in the near future? The feature would be very convenient in many cases. Best regards, Nick Hensel -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun May 11 14:39:52 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 11 May 2025 10:39:52 -0400 Subject: Sharing my experience with Switch Expressions Message-ID: Hello Amber Dev Team, I just wanted to share an experiment that I ran a long while back that made refactoring sort of painful for me today. I have a sealed interface called Cell.java, which has a deep inheritance graph below it, all made up of datatypes that are very amenable for edge-case checking via exhaustiveness checking. Aka, enums, sealed interfaces, and records. And for records, I used LOTS of record patterns, to extract and use the attributes I needed exhaustively. The purpose was becase I was implementing a Path-Finding Algorithm, and I needed to know what type of cells were in front of me in order to decide the cost of going down a certain path (ex. walking on spikes uses up more stamina then empty flooring). Basically, checking the current cell, and the 2 in front of me. Originally, I implemented edge-case checking via a nested set of switch expressions, all feeding into 1 final variable as the output. Using this, I would check the data types of my current cell and the 2 in front of me, to cover all possible combinations of Cell instances. While this worked well enough, there were 2 problems with it. 1 - The verticality of it made it a little difficult to keep track of what exactly I was checking at any given point in time. Sometimes, I would accidentally skip over c2, for example, because I would be 5 levels deep in a switch, checking the 3rd record attribute on c1, and then accidentally skip c2 and just jump to c3. 2 - There were a lot of keywords getting in the way of the logic, making it harder to read. So, I thought to myself -- why don't I just flatten this hierarchy down to a single switch expression? Patterns compose, after all. What I did was make a local record Triple(Cell c1, Cell c2, Cell c3){}, then fed that into a single Switch Expression, then checked all that I needed to using Record Patterns. This definitely solved both of my problems. For 1, I used whitespace to vertically align all of the record patterns for c1, c2, and c3. That made it impossible for me to miss a Cell. And for 2, having a single case contain nothing but record patterns meant that all of the fluff was out of the way. Only the logic in front of me. However, I exchanged 2 problems for 1. A few days ago, I needed to do some refactoring to add a new feature to the game. That meant adding 2 new data types (at different levels of my hierarchy) to my Cell. Well, when I did so, and pressed compile, I got the error message that I was missing a case on my switch expression. Perfect, that's what I love about Exhaustiveness Checking. The compiler becomes a "pit of success" of sorts, where you press compile, and all the missing cases become highlighted. Except, that's not really what happened. All the error message told me was that this switch was not exhaustive. Nothing about what I might be missing, or an example of a possible, unaccounted for case. I started searching and making changes where I thought be missing cases, and found that it was actually super difficult to find all of the missing spots. It took me almost 20-30 minutes just to see what I was missing. Back when I had nested Switch Expressions, finding the error case was trivial. Yes, it was the exact same error message, but since each Switch Expression was so small and self-contained, no real digging had to occur to figure out what case I was missing. And that's when I realized my tradeoff -- flattening my switch expressions meant that I had sacrificed traceability for readability. Finding my error got much harder, now that I had made my code much more readable. This was a bit frustrating to me, as I felt like I was forced to pick between 2 unideal situations. Anyways, readability is important to me, but traceability is even moreso, so I will go back to nested Switch Expressions. I just wanted to announce my experience, in case it helps. Thank you for your time and attention! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun May 11 14:48:36 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 11 May 2025 10:48:36 -0400 Subject: Sharing my experience with Switch Expressions In-Reply-To: References: Message-ID: Let me also add one thing. > I had started this project before Record Patterns had been released. So, it was only incidentally that I had ended up using a set of nested Switch Expressions -- I was forced to back then. I know for a fact that, had I been given the opportunity, I would have done things flat from the beginning. Nesting switch expressions is definitely not my first choice. -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Sun May 11 15:38:26 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Sun, 11 May 2025 15:38:26 +0000 Subject: Sharing my experience with Switch Expressions In-Reply-To: References: Message-ID: Hi David, I wonder if your code is open for review. If you can provide your actual code, I think we can have a better grasp of the whole situation. Regards, Chen Liang ________________________________ From: amber-dev on behalf of David Alayachew Sent: Sunday, May 11, 2025 9:48 AM To: amber-dev Subject: Re: Sharing my experience with Switch Expressions Let me also add one thing. I had started this project before Record Patterns had been released. So, it was only incidentally that I had ended up using a set of nested Switch Expressions -- I was forced to back then. I know for a fact that, had I been given the opportunity, I would have done things flat from the beginning. Nesting switch expressions is definitely not my first choice. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun May 11 19:22:57 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 11 May 2025 15:22:57 -0400 Subject: Sharing my experience with Switch Expressions In-Reply-To: References: Message-ID: Hey Chen, Sure. Here is the old nested Switch version. https://github.com/davidalayachew/HelltakerPathFinder/blob/12c57dab041924192b8613075ff6966b1b159e91/src/main/java/HelltakerPathFinderModule/HelltakerPathFinderPackage/Board.java#L511 And here is the new flattened switch version. https://github.com/davidalayachew/HelltakerPathFinder/blob/b0e8359e429967758b1ded02b818b8d46ed05b32/src/main/java/HelltakerPathFinderModule/HelltakerPathFinderPackage/Board.java#L512 I specifically made these links so that they link to the commit. So, you can browse the files, as they were at the time of that commit. But lol, hopefully looking at these can show you the night and day difference that I am talking about in readability? Or the ability to forget a case in the nested way? Flattened way outright FORBIDS me forgetting a case BECAUSE record patterns in Switch demand exhaustiveness. Semantically speaking, I almost feel like the flattened version is more accurate of my true intention, and less likely to be wrong. On Sun, May 11, 2025 at 11:38?AM Chen Liang wrote: > Hi David, > I wonder if your code is open for review. If you can provide your actual > code, I think we can have a better grasp of the whole situation. > > Regards, Chen Liang > ------------------------------ > *From:* amber-dev on behalf of David > Alayachew > *Sent:* Sunday, May 11, 2025 9:48 AM > *To:* amber-dev > *Subject:* Re: Sharing my experience with Switch Expressions > > Let me also add one thing. > > > I had started this project before Record Patterns had been released. So, > it was only incidentally that I had ended up using a set of nested Switch > Expressions -- I was forced to back then. > > I know for a fact that, had I been given the opportunity, I would have > done things flat from the beginning. Nesting switch expressions is > definitely not my first choice. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun May 11 19:53:11 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 11 May 2025 15:53:11 -0400 Subject: Sharing my experience with Switch Expressions In-Reply-To: References: Message-ID: Sorry, the flattened version I linked to included a default clause at the bottom. Leftover from when I was debugging. Here is one that is exhaustive without a default clause. https://github.com/davidalayachew/HelltakerPathFinder/blob/10b7a9d5fc4f6f563a68824de9a70735c7533226/src/main/java/HelltakerPathFinderModule/HelltakerPathFinderPackage/Board.java#L512 And fun fact -- debugging my missing case was so difficult, that to speed things up, I wrote a little script to print out all major permutations of my cases, then used *String* pattern-matching to eliminate the cases lol. Object pattern-matching was taking too long to debug so I had to go back to Strings and play whack a mole that way lol. Let me know if you need more details. On Sun, May 11, 2025 at 3:22?PM David Alayachew wrote: > Hey Chen, > > Sure. > > Here is the old nested Switch version. > > > https://github.com/davidalayachew/HelltakerPathFinder/blob/12c57dab041924192b8613075ff6966b1b159e91/src/main/java/HelltakerPathFinderModule/HelltakerPathFinderPackage/Board.java#L511 > > And here is the new flattened switch version. > > > https://github.com/davidalayachew/HelltakerPathFinder/blob/b0e8359e429967758b1ded02b818b8d46ed05b32/src/main/java/HelltakerPathFinderModule/HelltakerPathFinderPackage/Board.java#L512 > > I specifically made these links so that they link to the commit. So, you > can browse the files, as they were at the time of that commit. > > But lol, hopefully looking at these can show you the night and day > difference that I am talking about in readability? Or the ability to forget > a case in the nested way? Flattened way outright FORBIDS me forgetting a > case BECAUSE record patterns in Switch demand exhaustiveness. Semantically > speaking, I almost feel like the flattened version is more accurate of my > true intention, and less likely to be wrong. > > > > > > On Sun, May 11, 2025 at 11:38?AM Chen Liang > wrote: > >> Hi David, >> I wonder if your code is open for review. If you can provide your actual >> code, I think we can have a better grasp of the whole situation. >> >> Regards, Chen Liang >> ------------------------------ >> *From:* amber-dev on behalf of David >> Alayachew >> *Sent:* Sunday, May 11, 2025 9:48 AM >> *To:* amber-dev >> *Subject:* Re: Sharing my experience with Switch Expressions >> >> Let me also add one thing. >> >> >> I had started this project before Record Patterns had been released. So, >> it was only incidentally that I had ended up using a set of nested Switch >> Expressions -- I was forced to back then. >> >> I know for a fact that, had I been given the opportunity, I would have >> done things flat from the beginning. Nesting switch expressions is >> definitely not my first choice. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: