From alex.buckley at oracle.com Tue Jun 13 17:57:30 2023 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 13 Jun 2023 10:57:30 -0700 Subject: Method Chaining (enhancing Java syntax) In-Reply-To: <0F2DB659-9B60-484C-8C69-0F6867D02A92@anneca.cz> References: <7e98d0c3-632b-8476-3b78-ee77aa1b02b2@oracle.com> <0F2DB659-9B60-484C-8C69-0F6867D02A92@anneca.cz> Message-ID: <1028a73f-1a61-29c9-3c56-c72e6862d886@oracle.com> // Changed recipient from jdk-dev to amber-dev A couple of thoughts: 1. A `this` method typically makes progress by mutating the receiver, yet the arc of the Java language bends toward less mutability, not more mutability. 2. The scope of the feature is sufficiently limited, compared to self types, that it can sidestep the soundness problems raised by Maurizio. Limited scope helps the feature but also hurts it. The positive: by polishing covariant return to work better for deep class hierarchies, you improve abstraction for clients of the deep class hierarchy -- somewhat akin to sealed classes. The negative: polishing covariant return is pretty niche, yet everyone would have to learn about it (in case they find it in someone's code). Project Coin didn't move forward with method changing in Java 7 for basically the same "helpful, but not enough" reason. Alex On 6/13/2023 10:17 AM, Tom?? Ble?a wrote: > Hi, > > thank you for comments and references. I didn?t mean we should create > special self-type. The proposed syntax: > > ? public *this* hello() {} > > doesn?t mean ?/returns an object of the same type as my class/?. > > but rather > > ?/returns the same instance it is called on/?. The fact that it is also > the same type is a useful byproduct. Please note that I used lowercase T > to emphasize it is the instance not type. > > Under the hood nothing has to be returned (on the call stack) and the > method should compile to: > > ? public *void* hello() {} > > That?s why using this (ThisClass/MyClass) in method arguments or other > places doesn?t make sense to me. (Passing the same instance to method > which already has this reference.) It also doesn?t make sense (or > little) to use it on static methods like: > > ? public *static this* hello(); > > *Ad Generics) *As you pointed out it doesn?t work well with deep class > hierarchy and also reduces readability (adds boilerplate). Imagine for > example big library representing UI (like DOM structure): > > UIElement// method css(?) here > ? ? UILayout > ? ? ? ? UIGridLayout > ? ? ? ? ? ? ? > ? ? UIFormElement > ? ? ? ? UISelect > ? ? ? ? ? ? UIMultiSelect?// has method maxSelection() > > // compilation error > var mySelect = new UIMultiSelect().css(?color?,??green?) > .addClass(?attention?).maxSelection(3); > > This is basically my use-case. I have 60..100 subclasses. > > *Ad "Is the feature important enough?")* > > Technically all language features are just ?a syntactic sugar? to the > assembler (bytecode). I lived many years without switch-expressions, > try-with-resource,? :-) > > All the best, > Tomas > >> 13. 6. 2023 v 12:51, Maurizio Cimadamore >> >: >> >> Hi, >> the topic you mention is sometimes known, in type-system literature, >> as ?self types?. The topic of adding self types to Java was >> reasearched few years ago (see [1, 2]). While adding self-types >> increases the expressiveness of the language (e.g. allowing support >> for so called ?binary methods? - e.g. typing methods such as >> Object::equals correctly), such extension makes the language unsound. >> Given this declaration (which I borrow from the cited paper): >> >> |class Node { ... public void setNext(ThisClass next) { ... } } | >> >> And given the following generic method declaration: >> >> |static void setNextStatic(Node node1, Node node2) { >> node1.setNext(node2); // whooops } | >> >> Here we have an issue, because it is now possible to pass two >> instances of Node with different ?self types?, and this program would >> still type-check. In other words, subtyping here works against us - >> because I can declare some subclass of Node, e.g. MyColoredNode <: >> Node, and then call |setNextStatic(new Node(), new MyColoredNode())|, >> which clearly violates the specification of Node::setNext, but the >> type system is not powerful enough to detect that. >> >> Typically this tension is resolved by adding support for so called >> ?exact types? (denoted as @T). A variable with an exact type >> guarantees that it can only ever hold values that are the same as its >> static type. So, e.g. assigning a MyColoredNode object to a variable >> of type @Node would fail. Equipped with this new weapon, we can go >> back and change the declaration of setNext to: >> >> |public void setNext(@ThisClass next) { ... } | >> >> Which makes things sound again. >> >> This is a common tale when coming up with new language ideas - e.g. we >> start from a relatively constrained problems (e.g. how to better >> express covariant returns, in a way that doesn?t force us to >> re-override a method in all subclasses?) and we find something that >> works. Then we see what happens if we use the same construct in places >> we did not anticipate (in this case, usage of |this| type in parameter >> position) and see what happens. Almost every non-trivial type-system >> extension I?ve been working with in the past 15 years starts showing >> some kind of subtle interactions when looked at it that way. And >> ?this? is no exception. >> >> At which point the question becomes: is the feature important enough >> to either (a) swallow the cost of any other dependent feature it >> brings about (e.g. exact @ types), or (b) restrict the usage of the >> feature only in the places where it is well behaved (e.g. return >> position) ? The former has a complexity cost (introduction of a new >> kind of type), whereas the latter has an irregularity cost (e.g. the >> new types can only be used in few selected places). My (subjective) >> judgment is that, in this case the feature you propose is not worth >> paying any of these costs, given that there are some alternatives to >> achieve the same thing. >> >> On that subject, one alternative you have left out in your description >> is to use the generic type system to implement a poor man ?self type? >> - e.g. >> >> |class Foo> { T get(); } class SubFoo extends >> Foo { } | >> >> This is not perfect (especially when working with complex class >> hierarchies) but I have seen this technique used with success many >> times (e.g. in the implementation of the Stream API itself). >> >> Cheers >> Maurizio >> >> [1] - https://link.springer.com/chapter/10.1007/978-3-540-24851-4_18 >> [2] - https://www.sciencedirect.com/science/article/pii/S0167642313000038 >> >> On 09/06/2023 17:35, Tom?? Ble?a wrote: >> >> >> >>> //* I sent the following to discuss@ mailing list yesterday. (wrong >>> list for the topic) I hope this will be more appropriate mailing list *// >>> >>> Hi all, >>> >>> this is a request for feedback on a topic that has been on my mind >>> for a few weeks. I have written a short document in JEP format and >>> would like to ask you to comment if you find the described proposal >>> useful. >>> >>> Thanks, >>> Tomas Blesa >>> __________________________________ >>> >>> Summary >>> ------- >>> Enhance the Java language syntax to better support the method >>> chaining (named parameter idiom) programming pattern. >>> >>> Goals >>> ----- >>> The primary goal is to remove unnecessary boilerplate code in class >>> methods designed for type-safe chained calls, especially when >>> combined with inheritance. >>> >>> Motivation >>> ---------- >>> [Method chaining](https://en.wikipedia.org/wiki/Method_chaining) is a >>> widely used and popular programming pattern, particularly in creating >>> libraries (APIs) or configuration objects. Programmers can easily >>> create a method that returns `this` with a method signature that >>> specifies the returning type of the containing class. >>> >>> ```java >>> class Shape { >>> ? ? public Shape scale(double ratio) { >>> ? ? ? ? // recalculate all points >>> return this; >>> ? ? } >>> } >>> ``` >>> >>> The problem arises when we combine this pattern with inheritance. We >>> can lose type information when calling the method on a subclass. For >>> example, let's create two subclasses of the `Shape` superclass: >>> >>> ```java >>> class Rectangle extends Shape { >>> ? ? public Rectangle roundCorners(double pixels) { >>> ? ? ? ? // ... >>> return this; >>> ? ? } >>> } >>> >>> class Circle extends Shape { >>> } >>> ``` >>> >>> Now, imagine the following piece of code using the mini-library above: >>> >>> ```java >>> var myRect = new Rectangle().scale(1.2).roundCorners(10); >>> ``` >>> >>> The code won't compile because `scale()` returns the type `Shape`, >>> which doesn't have the `roundCorners` method. There is also a problem >>> even without the final `roundCorners()` call: >>> >>> ```java >>> var myRect = new Rectangle().scale(1.2); >>> ``` >>> >>> The inferred type of `myRect` is `Shape` and not `Rectangle`, so the >>> following line will also be invalid: >>> >>> ```java >>> myRect.roundCorners(10); >>> ``` >>> >>> Straightforward solutions to the problem could be: >>> >>> 1) Override the `scale()` method in all subclasses and change the >>> return type: >>> >>> ```java >>> class Rectangle extends Shape { >>> ? ? // ... >>> @Override >>> ? ? public Rectangle scale(double ratio) { >>> super.scale(ratio); >>> return this; >>> ? ? } >>> } >>> ``` >>> >>> 2) Split object construction and method calls: >>> >>> ```java >>> var myRect = new Rectangle(); >>> myRect.scale(1.2); >>> myRect.roundCorners(10); >>> ``` >>> >>> 3) Partial solution - reorder chained calls (if possible): >>> >>> ```java >>> var myRect = new Rectangle(); >>> myRect.roundCorners(10).scale(1.2); // roundCorners called first >>> ``` >>> >>> All of these solutions add unnecessary lines of code, and as the >>> library of shapes grows, keeping the desired return type will >>> introduce more and more boilerplate code. >>> >>> Description >>> ----------- >>> The proposed solution to the problem described in the previous >>> section is to extend the Java syntax for the returned type in method >>> signatures: >>> >>> ```java >>> class Shape { >>> ? ? public this scale(double ratio) { // <=== returns this >>> ? ? ? ? // recalculate all points >>> return this; >>> ? ? } >>> } >>> ``` >>> >>> Methods declared or defined as returning `this` can only return the >>> instance on which they are called. The following code will be >>> type-safe and perfectly valid: >>> >>> ```java >>> var myRect = ? ? ? ? ? ? ? ? ? ? // inferred Rectangle type >>> ? ? new Rectangle() ? ? ? ? ? ? ?// returns Rectangle instance >>> .scale(1.2) ? ? ? ? ? ? ? ? ?// returns Rectangle instance >>> .roundCorners(10); ? ? ? ? ? // returns Rectangle instance >>> ``` >>> >>> The constructed type `Rectangle` is preserved throughout the entire >>> call chain. >>> >>> It is possible to override methods returning `this`, but the >>> subclass' implementation must also be declared with the `this` >>> keyword instead of a concrete returning type. >>> >>> It is even possible to remove the explicit return statement altogether: >>> >>> ```java >>> class Shape { >>> ? ? public this scale(double ratio) { >>> ? ? ? ? // recalculate all points >>> ? ? } >>> } >>> ``` >>> >>> Or simply remove the value `this` from the return statement: >>> >>> ```java >>> class Shape { >>> ? ? public this scale(double ratio) { >>> ? ? ? ? // recalculate all points >>> ? ? ? ? if (condition) return; ? ? ? ? // <== automatically returns this >>> ? ? ? ? // do something else >>> ? ? } >>> } >>> ``` >>> >>> In fact, methods returning `this` can be compiled to the same >>> bytecode as methods returning `void`. This is because the instance >>> reference (and the returned value) is already known to the caller, >>> eliminating the need to pass that value back through the call stack. >>> As a result, both CPU cycles and memory are saved. >>> >>> In the Java world, it is common to create getters and setters >>> according to the Java Beans specification in the form of >>> `getProperty`/`setProperty` pairs or `isProperty`/`setProperty`. >>> Setters are defined as returning `void`. These setters can be more >>> useful if defined as returning `this`: >>> >>> ```java >>> class Customer { >>> ? ? public this setFirstname() { ... } >>> ? ? public this setSurname() { ... } >>> ? ? public this setEmail() { ... } >>> } >>> ``` >>> >>> This allows for more concise usage when constructing and configuring >>> an instance without adding more code: >>> >>> ```java >>> customers.add( >>> ? ? new Customer() >>> .setFirstname(resultSet.getString(1)) >>> .setSurname(resultSet.getString(2)) >>> .setEmail(resultSet.getString(3)) >>> ); >>> ``` >>> >>> It is also possible to declare an interface with methods returning >>> `this`: >>> >>> ```java >>> interface Shape { >>> ? ? this scale(double ratio); >>> } >>> ``` >>> >>> In this case, all implementing classes must define the method as >>> returning `this`. >>> >>> The proposed syntax is a bit less useful for enums or records, as >>> neither of them allows for inheritance. But enums and records can >>> also implement interfaces and for this reason and for overall >>> consistency, "return this" syntax should be allowed for enums and >>> records. >>> >>> To accommodate the syntax with the Java Reflection API, it will >>> probably be required to create a special final placeholder class >>> `This` (with an uppercase "T"), similar to `java.lang.Void`. >>> >>> Alternatives >>> ------------ >>> It is probably possible to help auto-generate overriding methods in >>> subclasses using annotation processing, but this option wasn't fully >>> explored. However, such an approach would add extra unnecessary code >>> to compiled subclasses and go against the primary goal of reducing >>> boilerplate. >>> >>> Risks and Assumptions >>> --------------------- >>> The proposed syntax is likely to break the compatibility of >>> library-dependent code whose author decides to switch to the "return >>> this" syntax between versions. >>> >>> Older code that looks like this: >>> >>> ```java >>> class MyUglyShape extends Shape { >>> @Override >>> ? ? public MyUglyShape scale(double ratio) { >>> return this; >>> ? ? } >>> } >>> ``` >>> >>> will have to be rewritten as: >>> >>> ```java >>> class MyUglyShape extends Shape { >>> @Override >>> ? ? public this scale(double ratio) { ? ?// signature change >>> ? ? ? ? // optional removal of the return this statement >>> ? ? } >>> } >>> ``` >>> >>> or >>> >>> ```java >>> class MyUglyShape extends Shape { >>> // override removed >>> } >>> ``` >>> >>> This problem can be mitigated with the help of smart IDEs >>> automatically suggesting such refactoring. >>> >>> Another possible risk is breaking old code that relies on the >>> Reflection API for scanning the returning types of methods. >> >> >> ? > From tanksherman27 at gmail.com Thu Jun 15 12:39:54 2023 From: tanksherman27 at gmail.com (Julian Waters) Date: Thu, 15 Jun 2023 20:39:54 +0800 Subject: Presentation of new Amber JEPs Message-ID: Hi all, Where would a new JEP (and the discussions around it) targeted at Amber first be conceptualized? I'm not sure if the official JIRA is the place to go for this best regards, Julian -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jun 15 14:41:01 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Jun 2023 10:41:01 -0400 Subject: Presentation of new Amber JEPs In-Reply-To: References: Message-ID: <9389d200-ac8d-0dd4-aa48-66e9bc500471@oracle.com> What are you trying to do? Are you looking for discussions around existing JEPs, or do you have a feature idea in your pocket you are trying to socialize? On 6/15/2023 8:39 AM, Julian Waters wrote: > Hi all, > > Where would a new JEP (and?the discussions around it) targeted at > Amber first be conceptualized? I'm not sure if?the official JIRA > is?the place?to go for?this > > best regards, > Julian -------------- next part -------------- An HTML attachment was scrubbed... URL: From tanksherman27 at gmail.com Thu Jun 15 16:53:06 2023 From: tanksherman27 at gmail.com (Julian Waters) Date: Fri, 16 Jun 2023 00:53:06 +0800 Subject: Presentation of new Amber JEPs In-Reply-To: <9389d200-ac8d-0dd4-aa48-66e9bc500471@oracle.com> References: <9389d200-ac8d-0dd4-aa48-66e9bc500471@oracle.com> Message-ID: Hi Brian, it's the latter On Thu, Jun 15, 2023 at 10:41?PM Brian Goetz wrote: > What are you trying to do? Are you looking for discussions around > existing JEPs, or do you have a feature idea in your pocket you are trying > to socialize? > > On 6/15/2023 8:39 AM, Julian Waters wrote: > > Hi all, > > Where would a new JEP (and the discussions around it) targeted at Amber > first be conceptualized? I'm not sure if the official JIRA is the place to > go for this > > best regards, > Julian > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jun 15 19:06:41 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Jun 2023 15:06:41 -0400 Subject: Presentation of new Amber JEPs In-Reply-To: References: <9389d200-ac8d-0dd4-aa48-66e9bc500471@oracle.com> Message-ID: <582f93f6-d3d6-6a56-b3f5-6d1a956492a5@oracle.com> Figured as much :) So, the place to start is *not* with a JEP (or worse, with a PR).? It's not even with a description of the feature you have in mind; it's with a discussion of a _problem_, and why it is a problem, and why it should be solved in the language, and only secondarily how the proposed solution might possibly in some form address the problem.? You'll want to build mindshare around around why this problem needs is real, and why it needs to be solved in the language. This is a very, very high bar, and if you clear it, then you're basically the first 0% of the way there :) On 6/15/2023 12:53 PM, Julian Waters wrote: > Hi Brian, it's the latter > > On Thu, Jun 15, 2023 at 10:41?PM Brian Goetz > wrote: > > What are you trying to do?? Are you looking for discussions around > existing JEPs, or do you have a feature idea in your pocket you > are trying to socialize? > > On 6/15/2023 8:39 AM, Julian Waters wrote: >> Hi all, >> >> Where would a new JEP (and?the discussions around it) targeted at >> Amber first be conceptualized? I'm not sure if?the official JIRA >> is?the place?to go for?this >> >> best regards, >> Julian > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Thu Jun 15 22:01:58 2023 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Thu, 15 Jun 2023 23:01:58 +0100 Subject: JEP 430 String Templates, some weird scenarios feedback Message-ID: I understand why it works like that but it still causes some confusion to me, at least it should be somehow documented: var val = 1; var template = RAW."val = \{val}"; assert template.interpolate().equals("val = 1"); val = 2; assert template.interpolate().equals("val = 1");//weird Another thing, I could not figure out a reasonable conditional template syntax and nesting. This looks kind of ugly and hard to read String msg(String name, String val) { return STR."Today is a nice day. \{name == null ? "" : STR."Hello \{name}! " }You have \{val} thing."; } assert msg(null, "this").equals("Today is a nice day. You have this thing." ); assert msg("World", "that").equals("Today is a nice day. Hello World! You have that thing."); This thing would be especially important e.g. for a JSON tree templating. -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jun 15 22:47:07 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 15 Jun 2023 18:47:07 -0400 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: References: Message-ID: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> On 6/15/2023 6:01 PM, Anatoly Kupriyanov wrote: > I understand why it works like that but it still causes some confusion > to me, at least it should be somehow documented: > > var val = 1; var template = RAW."val = \{val}"; assert > template.interpolate().equals("val = 1"); > val = 2; assert template.interpolate().equals("val = 1");//weird Yes, a template closes over values, not variables.? We could adopt the same constraint as we do with lambdas / inner classes -- require that any referenced variables be effectively final -- but this would likely be quite restrictive given the typical use cases for string templates. > > Another thing, I could not figure out a reasonable conditional > template syntax and nesting. This looks kind of ugly and hard to read > > String msg(String name, String val) > { return STR."Today is a nice day. \{name == null ? "" : STR."Hello > \{name}! "}You have \{val}thing."; > } > assert msg(null, "this").equals("Today is a nice day. You have this > thing."); assert msg("World", "that").equals("Today is a nice day. > Hello World! You have that thing."); > > This thing would be especially important e.g. for a JSON tree templating. > Whenever you have an expression that is so complicated or deeply nested that it is hard to read, the standard trick is to break it up.? This is not unique to string templates. ??? var n = (name == null) ? "" : STR."Hello \{name}! "; ??? return STR."Today is a nice day. \{n}? You have \{val} thing."; -------------- next part -------------- An HTML attachment was scrubbed... URL: From tanksherman27 at gmail.com Fri Jun 16 00:20:49 2023 From: tanksherman27 at gmail.com (Julian Waters) Date: Fri, 16 Jun 2023 08:20:49 +0800 Subject: Presentation of new Amber JEPs In-Reply-To: <582f93f6-d3d6-6a56-b3f5-6d1a956492a5@oracle.com> References: <9389d200-ac8d-0dd4-aa48-66e9bc500471@oracle.com> <582f93f6-d3d6-6a56-b3f5-6d1a956492a5@oracle.com> Message-ID: I see, but where would the appropriate place for such discussion be? Since JEPs aren't where they take place, where do the beginnings of ideas targeted at Amber (and really any Project, like Valhalla) which eventually become JEPs begin? I don't think they start purely on the mailing lists alone On Fri, Jun 16, 2023 at 3:06?AM Brian Goetz wrote: > Figured as much :) > > So, the place to start is *not* with a JEP (or worse, with a PR). It's > not even with a description of the feature you have in mind; it's with a > discussion of a _problem_, and why it is a problem, and why it should be > solved in the language, and only secondarily how the proposed solution > might possibly in some form address the problem. You'll want to build > mindshare around around why this problem needs is real, and why it needs to > be solved in the language. > > This is a very, very high bar, and if you clear it, then you're basically > the first 0% of the way there :) > > > > On 6/15/2023 12:53 PM, Julian Waters wrote: > > Hi Brian, it's the latter > > On Thu, Jun 15, 2023 at 10:41?PM Brian Goetz > wrote: > >> What are you trying to do? Are you looking for discussions around >> existing JEPs, or do you have a feature idea in your pocket you are trying >> to socialize? >> >> On 6/15/2023 8:39 AM, Julian Waters wrote: >> >> Hi all, >> >> Where would a new JEP (and the discussions around it) targeted at Amber >> first be conceptualized? I'm not sure if the official JIRA is the place to >> go for this >> >> best regards, >> Julian >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From scolebourne at joda.org Fri Jun 16 08:56:43 2023 From: scolebourne at joda.org (Stephen Colebourne) Date: Fri, 16 Jun 2023 09:56:43 +0100 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> Message-ID: On Thu, 15 Jun 2023 at 23:49, Brian Goetz wrote: > On 6/15/2023 6:01 PM, Anatoly Kupriyanov wrote: > > I understand why it works like that but it still causes some confusion to me, at least it should be somehow documented: > > var val = 1; var template = RAW."val = \{val}"; assert template.interpolate().equals("val = 1"); > val = 2; assert template.interpolate().equals("val = 1");//weird > > > Yes, a template closes over values, not variables. We could adopt the same constraint as we do with lambdas / inner classes -- require that any referenced variables be effectively final -- but this would likely be quite restrictive given the typical use cases for string templates. FWIW, the example here pushes me to encourage you to adopt effectively final - and the parallel with lambdas means that no one will be surprised by choosing effectively final for templates. I am surprised by the claim that "typical use cases" make this restrictive. Can you expand? Given that the value is captured not the variable, it is hard to imagine use cases where effectively final would be problematic. If there are problematic use cases, perhaps it would be better to add a language change that gives the developer more control over effectively final (various options available), Stephen From forax at univ-mlv.fr Fri Jun 16 20:23:52 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 16 Jun 2023 22:23:52 +0200 (CEST) Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> Message-ID: <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> ----- Original Message ----- > From: "Stephen Colebourne" > To: "amber-dev" > Sent: Friday, June 16, 2023 10:56:43 AM > Subject: Re: JEP 430 String Templates, some weird scenarios feedback > On Thu, 15 Jun 2023 at 23:49, Brian Goetz wrote: >> On 6/15/2023 6:01 PM, Anatoly Kupriyanov wrote: >> >> I understand why it works like that but it still causes some confusion to me, at >> least it should be somehow documented: >> >> var val = 1; var template = RAW."val = \{val}"; assert >> template.interpolate().equals("val = 1"); >> val = 2; assert template.interpolate().equals("val = 1");//weird >> >> >> Yes, a template closes over values, not variables. We could adopt the same >> constraint as we do with lambdas / inner classes -- require that any referenced >> variables be effectively final -- but this would likely be quite restrictive >> given the typical use cases for string templates. > > FWIW, the example here pushes me to encourage you to adopt effectively > final - and the parallel with lambdas means that no one will be > surprised by choosing effectively final for templates. > > I am surprised by the claim that "typical use cases" make this > restrictive. Can you expand? Given that the value is captured not the > variable, it is hard to imagine use cases where effectively final > would be problematic. If there are problematic use cases, perhaps it > would be better to add a language change that gives the developer more > control over effectively final (various options available), It will not be convenient for all template processors used for logging/printing. LOGGER.trace()."\(thisVariableMustNowBeEffectivelyFinal)"; > > Stephen regards, R?mi From brian.goetz at oracle.com Sat Jun 17 00:03:49 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 16 Jun 2023 20:03:49 -0400 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <0d6b0ab6-37b3-0d22-3f12-573d41229218@oracle.com> More generally, any variable that is today used in string concat, String::format, or println is a candidate for use in a template.? Many, many of these will not be effectively final. On 6/16/2023 4:23 PM, Remi Forax wrote: > ----- Original Message ----- >> From: "Stephen Colebourne" >> To: "amber-dev" >> Sent: Friday, June 16, 2023 10:56:43 AM >> Subject: Re: JEP 430 String Templates, some weird scenarios feedback >> On Thu, 15 Jun 2023 at 23:49, Brian Goetz wrote: >>> On 6/15/2023 6:01 PM, Anatoly Kupriyanov wrote: >>> >>> I understand why it works like that but it still causes some confusion to me, at >>> least it should be somehow documented: >>> >>> var val = 1; var template = RAW."val = \{val}"; assert >>> template.interpolate().equals("val = 1"); >>> val = 2; assert template.interpolate().equals("val = 1");//weird >>> >>> >>> Yes, a template closes over values, not variables. We could adopt the same >>> constraint as we do with lambdas / inner classes -- require that any referenced >>> variables be effectively final -- but this would likely be quite restrictive >>> given the typical use cases for string templates. >> FWIW, the example here pushes me to encourage you to adopt effectively >> final - and the parallel with lambdas means that no one will be >> surprised by choosing effectively final for templates. >> >> I am surprised by the claim that "typical use cases" make this >> restrictive. Can you expand? Given that the value is captured not the >> variable, it is hard to imagine use cases where effectively final >> would be problematic. If there are problematic use cases, perhaps it >> would be better to add a language change that gives the developer more >> control over effectively final (various options available), > It will not be convenient for all template processors used for logging/printing. > > LOGGER.trace()."\(thisVariableMustNowBeEffectivelyFinal)"; > >> Stephen > regards, > R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From scolebourne at joda.org Sat Jun 17 10:02:31 2023 From: scolebourne at joda.org (Stephen Colebourne) Date: Sat, 17 Jun 2023 11:02:31 +0100 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: <0d6b0ab6-37b3-0d22-3f12-573d41229218@oracle.com> References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> <0d6b0ab6-37b3-0d22-3f12-573d41229218@oracle.com> Message-ID: On Sat, 17 Jun 2023 at 01:03, Brian Goetz wrote: > > More generally, any variable that is today used in string concat, String::format, or println is a candidate for use in a template. Many, many of these will not be effectively final. OK the logging example is reasonable. And the OP used `interpolate()` directly which is non-standard. Maybe the messaging needs to be that holding onto an instance of the template is dubious outside of frameworks? Having an accessible template implies you can do something useful with it, when really the best use for it is to pass to a framework-level method.. Having reviewed it more, I think my real issue here is different - the "template" terminology is very misleading to me. IMO it is NOT a template at all, as everything is captured once. A template is a "thing with holes", where you fill in the holes before final use. There is no `withValues(List)` method to swap the values that I can see. So I struggle to see how it can be called a "template". The API design seems to have two concepts smooshed together - a proper template, and a "bound template". // alternative API design - StringTemplate only contains the fragments // BoundStringTemplate is equivalent to StringTemplate today StringTemplate st = StringTemplate.parse("Hello \{} \{}"); BoundStringTemplate bt = st.bind("dear", "friend); String result = processor.process(bt); | Hello dear friend // syntax sugar String s = STR."Hello \{a} \{b}"; String t = StringTemplate.parse("Hello \{} \{}").bind(a, b).process(STR); Note how this API design does not have to change the Java syntax of template expressions at all. Nor does it affect the safety or method handle part of the JEP AFAICT. Note how the syntax sugar explanation makes it obvious that `bind()` captures the values, not the variables. Reading the Javadoc, it is unclear why `interpolate()` exists. The `process()` method appears to be the main standard entry point which safely produces the expected outcome. The Javadoc could be read to imply that `interpolate()` just joins the fragments and values together into a String without validation. Even if it is validated, it seems odd to offer a way of producing a String if the natural type of the template is not a String. Stephen From brian.goetz at oracle.com Sat Jun 17 13:40:46 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 17 Jun 2023 09:40:46 -0400 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> <0d6b0ab6-37b3-0d22-3f12-573d41229218@oracle.com> Message-ID: <009d9e3d-22e4-edfa-1402-d1516a10a588@oracle.com> On 6/17/2023 6:02 AM, Stephen Colebourne wrote: > On Sat, 17 Jun 2023 at 01:03, Brian Goetz wrote: >> More generally, any variable that is today used in string concat, String::format, or println is a candidate for use in a template. Many, many of these will not be effectively final. > OK the logging example is reasonable. And the OP used `interpolate()` > directly which is non-standard. Maybe the messaging needs to be that > holding onto an instance of the template is dubious outside of > frameworks? I don't think it is dubious, you just have to understand what you have in your hands. For example, if I did: ??? int x = 2; ??? StringBuilder sb = new StringBuilder().append("Hi ").append(x); ??? x = 3; No one would be surprised in the least that when we eventually call toSTring on the StringBuilder, that we captured the value 2 rather than the variable x.? The same if we had done list.add(x); this puts the value of x in the list.? People understand this. The RAW processor is kind of the oddball here, in that it returns its template argument (the carrier for string framgements and values) unprocessed, so it can then be used later (or not, or multiple times) to build with an as-yet-unspecified processor. While I get that people could think "this is like a lambda", it's not; it's more like a (highly structured) method call that returns a builder-like object.? We just have to help people connect to the mental model they already have.? Suggestions for doing so are welcome. > Having an accessible template implies you can do something > useful with it, when really the best use for it is to pass to a > framework-level method.. Running a processor on it *is* useful!? You get to package up the template and decide later if, and how, to process it. > Having reviewed it more, I think my real issue here is different - the > "template" terminology is very misleading to me. IMO it is NOT a > template at all, as everything is captured once. A template is a > "thing with holes", where you fill in the holes before final use. The trouble with words is that they can have multiple meanings, and different people can have different beliefs about which is the "real" meaning.? Yes, *one* of the meanings is the "thing with holes", like those green flowchart templates IBM gave away for free in the 70s.? These are interesting because no matter how many times you use it to make a square on the paper, the template thingie is unmodified.? We might call these "immutable templates" or "factory templates". But another meaning is *a thing to be filled in*, like a Mad Libs. This is often a one-time? thing -- we don't erase a Mad Libs sheet and play it again.? (Well, some people might, but it's pretty clearly intended as a one-shot thing.)? And we also sometimes call the filled-in thing a template, as in "Fill out the Complaint Template and put it in the round file". (So in your above paragraph, you get it right the first three times, by saying "to me" and "IMO", but then in the end you come around to saying "A template *is*", which you follow up with: > The API design seems to have two concepts smooshed together - a proper > template, and a "bound template". where you reserve "proper" for "the association that comes most readily to Stephen's mind".? Would have been better to call them "unbound" and "bound" templates, since this is supposedly a discussion about "what does template mean to people" and not a "Stephen Imposes His World View" discussion.) But yes, the meaning of template we are using is the bound form, like a completed Mad Lib.? The RHS of a template expression (the stringy thing with embedded values) is the completed Mad Lib.? The processor takes the completed Mad Lib and does something with it, such as reading it to the crowd.? The RAW processor saves it for later, perhaps awaiting a more suitable audience. At root, I think what you're saying is "I think BoundTemplate or FilledInTemplate or TemplateWithValues might be a better name than StringTemplate", which is a totally reasonable opinion!? There's also a valid opinion about whether RAW is the right name for the processor that yields a StringTemplate; other candidates include LAZY or DEFER or QUOTED. In the current feature, the type StringTemplate serves two (well, maybe 1.5) uses.? In the "usual" case (e.g., STR."Hello \{foo}"), a StringTemplate is created and passed to the STR processor (invisible to the user code); the RAW processor helps that to "escape" back into the user code, where it can be used later.? I think this is probably the most confusing thing about it; mostly its an internal entity for consumption by the processor, but soemtimes we want to let it escape into our program. > // alternative API design - StringTemplate only contains the fragments Unfortunately this is not just an alternate API design; it's an alternate language design, because (among other reasons) \{} is not allowed in string literals today.? The \ can only be used with the defined set of escape characters such as \t. > Reading the Javadoc, it is unclear why `interpolate()` exists. The > `process()` method appears to be the main standard entry point which > safely produces the expected outcome. The Javadoc could be read to > imply that `interpolate()` just joins the fragments and values > together into a String without validation. Even if it is validated, it > seems odd to offer a way of producing a String if the natural type of > the template is not a String. It exists to serve toString() - like use cases, when you want to take an unprocessed template and describe it without processing it. Better names and/or descriptions are probably possible.? Perhaps there is a better name. So, what I take away from this discussion is that you'd like to consider some other names for StringTemplate and/or RAW and maybe interpolate? From scolebourne at joda.org Sun Jun 18 15:31:56 2023 From: scolebourne at joda.org (Stephen Colebourne) Date: Sun, 18 Jun 2023 16:31:56 +0100 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: <009d9e3d-22e4-edfa-1402-d1516a10a588@oracle.com> References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> <0d6b0ab6-37b3-0d22-3f12-573d41229218@oracle.com> <009d9e3d-22e4-edfa-1402-d1516a10a588@oracle.com> Message-ID: On Sat, 17 Jun 2023 at 14:40, Brian Goetz wrote: We have three descriptions of "template" so far: 1) a "thing with holes" 2) *a thing to be filled in* (an empty Mad Lib) 3) a completed Mad Lib The first two are the same surely? An immutable thing with specific places that data needs adding. My view is clear - when you fill in a Mad Lib, it is no longer a template. (You can fill in a template, but once filled in it is no longer a template, it is a customized document. If the template is mutable, like a piece of paper with a Mad Lib on it, then it ceases to be a template as soon as you write anything on it, because it ceases to be reusable. It is the aspect of reusability which is core here, so you can merge multiple different sets of values into the same template.) More importantly than our own opinions is that Java has had plenty of other libraries involving templates, which I contend match my definition of reusability: - JSP [1] - Freemarker [2] - Velocity [3] - Thymeleaf [4] - Antlr StringTemplate [5] [1] https://www.caucho.com/resin-3.1/doc/jsp-templates.xtp [2] https://freemarker.apache.org/ [3] https://velocity.apache.org/engine/2.3/developer-guide.html#the-fundamental-pattern [4] https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#what-kind-of-templates-can-thymeleaf-process [5] https://github.com/antlr/stringtemplate4/blob/master/doc/introduction.md#groups-of-templates All of these are about separating the template from the values/data/context. They vary in how the separation occurs, some are mutable, some allow embedded code or expressions The way I've always used it is that you start with a template and add in the actual values you want - the template is reusable for many different sets of values. (Some of these libraries are mutable, thus the values end up alongside the original template I really do think this API is pushing against a *lot* of Java history in calling the main class `StringTemplate` and the language feature a "template expression". > At root, I think what you're saying is "I think BoundTemplate or > FilledInTemplate or TemplateWithValues might be a better name than > StringTemplate", which is a totally reasonable opinion! Any of those three names, or others would be a lot clearer. Separately, I'm arguing that the "unbound" template should be exposed as well through the API. If you wanted to defer that, you could do so long as the terminology adopted now was "bound template" or similar. > No one would be surprised in the least that when we eventually call > toSTring on the StringBuilder, that we captured the value 2 rather than > the variable x. Agreed. But it is well understood because it looks like a method call. So there are two parts to the point here: 1) You've called it a template, which implies that the things inside are placeholder (aka variables), not values 2) It doesn't look like a method call, so the learned knowledge of values doesn't apply > Suggestions [...] welcome. My suggestion is to address the two points above: 1) Stop calling it a template (calling it bound template would be OK) 2) Provide a matching API where the values are passed into a method, then equate the expression as a kind of syntax sugar for the API. > So, what I take away from this discussion is that you'd like to consider > some other names for [..] interpolate? describe() would be a lot clearer. > There's also a > valid opinion about whether RAW is the right name for the processor that > yields a StringTemplate; other candidates include LAZY or DEFER or QUOTED. It is tricky to grasp. Because RAW and STR produce different things when they look similar - one is the result type and one the bound template type. Of course its not actually different at all, but it sure looks that way. I'm not convinced that changing to LAZY/DEFER/QUOTED makes any difference to the mental model issue. > > // alternative API design - StringTemplate only contains the fragments > Unfortunately this is not just an alternate API design; it's an > alternate language design, because (among other reasons) \{} is not > allowed in string literals today. True. Good job this is a preview feature then :-) FWIW I do feel the UPPER."" part of the syntax of this feature isn't helping understanding. But I'll avoid making a syntax suggestion unless asked to. thanks Stephen From brian.goetz at oracle.com Sun Jun 18 21:10:03 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 18 Jun 2023 17:10:03 -0400 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> <0d6b0ab6-37b3-0d22-3f12-573d41229218@oracle.com> <009d9e3d-22e4-edfa-1402-d1516a10a588@oracle.com> Message-ID: > On Sat, 17 Jun 2023 at 14:40, Brian Goetz wrote: > > We have three descriptions of "template" so far: > 1) a "thing with holes" > 2) *a thing to be filled in* (an empty Mad Lib) > 3) a completed Mad Lib > > The first two are the same surely? An immutable thing with specific > places that data needs adding. My view is clear - when you fill in a > Mad Lib, it is no longer a template. Surely not!? The first thing (also be called a "stencil"), which is used to stamp out identical shapes but the mutation happens elsewhere: you use the immutable template to mutate the paper. Whereas the second thing (also called a "form") is mutated when you fill it in.? This mutability is monotonic; the template goes from "blank" to "filled in", and not backwards.? (Naming things is hard.) Regardless, our string templates are like #3. >> At root, I think what you're saying is "I think BoundTemplate or >> FilledInTemplate or TemplateWithValues might be a better name than >> StringTemplate", which is a totally reasonable opinion! Great, I take your reply as concurring that this is your main concern :) > Separately, I'm arguing that the "unbound" template should be exposed > as well through the API. If you wanted to defer that, you could do so > long as the terminology adopted now was "bound template" or similar. That is designing an entirely other feature (with plenty of additional complexity.)? But, you can get the effect of that feature with the feature we have; Jim has posted examples of how to use string templates for things like mail merge. >> No one would be surprised in the least that when we eventually call >> toSTring on the StringBuilder, that we captured the value 2 rather than >> the variable x. > Agreed. But it is well understood because it looks like a method call. A template expression is like a method call (because it is sugar for one), so this is point about which education is needed. > 2) It doesn't look like a method call, so the learned knowledge of > values doesn't apply It looks more like a method call (see the dot!) than anything else in the language, and this choice was not accidental.? It will be unfamiliar the first time you see it, and people may make wrong guesses, but that's true of every new feature. > FWIW I do feel the UPPER."" part of the syntax of this feature isn't > helping understanding. But I'll avoid making a syntax suggestion > unless asked to. But the UPPER part isn't even part of this feature!? That comes from Java 1.0.? STR and RAW and friends are *just static final fields*, and the UPPER part comes from the long-standing naming convention that constants are ALL_CAPS. Indeed, we expect it to be a happy discovery when developers realize that STR is not language syntax [*], but just an ordinary constant of type TemplateProcessor.? You could, for example, do this: ??? TemplateProcessor alt = STR; ??? String s = alt."Hello \{name}"; The thing to the left of the dot is a receiver, plain and simple. Any expression of the correct type will do; it's as if "..." is a method.? (Which is why it has method call semantics.)? STR just happens to be a convenient constant for common behavior. [*] Some minor magic in that this field is auto-static-imported From scolebourne at joda.org Mon Jun 19 06:16:17 2023 From: scolebourne at joda.org (Stephen Colebourne) Date: Mon, 19 Jun 2023 07:16:17 +0100 Subject: JEP 430 String Templates, some weird scenarios feedback In-Reply-To: References: <0b06c3a7-3469-4c08-ffbb-fa9613f5a123@oracle.com> <217671587.83037682.1686947032414.JavaMail.zimbra@univ-eiffel.fr> <0d6b0ab6-37b3-0d22-3f12-573d41229218@oracle.com> <009d9e3d-22e4-edfa-1402-d1516a10a588@oracle.com> Message-ID: On Sun, 18 Jun 2023 at 22:10, Brian Goetz wrote: > But the UPPER part isn't even part of this feature! That comes from > Java 1.0. STR and RAW and friends are *just static final fields*, and > the UPPER part comes from the long-standing naming convention that > constants are ALL_CAPS. Just to note that I understood the static nature of the import. But the JEP really confuses matters by using JSON, THAI, DB and LOCALIZE as local variable names. I'd recommend changing those. Stephen From davidalayachew at gmail.com Wed Jun 21 19:12:33 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Wed, 21 Jun 2023 15:12:33 -0400 Subject: Typo in the Project Amber page Message-ID: Hello Amber Dev Team, I was rereading through some of the design documents here, and it appears that there is a typo. https://openjdk.org/projects/amber/ Ctrl+f "Type Patterns in switch" It says (September, but no year following after it. Thank you for your time and attention! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Wed Jun 21 21:40:00 2023 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Wed, 21 Jun 2023 22:40:00 +0100 Subject: Allow lamba-like synax for methods Message-ID: Not sure if it is the right mail list, please advise if there is a better place to give suggestions about Java language. I suggest using lamba-like syntax for method implementation. Suppose you have a code like this: final class Account { private final int id; private String name; public Account(int id) { this.id = id; } public int getId() { return id; } public String getName() { return name; } public Account setName(String name) { this.name = name; return this; } @Override public String toString() { return "Account{id=" + id + ", name='" + name + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Account account = (Account) o; return id == account.id; } @Override public int hashCode() { return Objects.hash(id); } } void transfer(Account from, Account to, int amount) { transfer(from, to, amount, 0); } void transfer(Account from, Account to, int amount, int commission) { System.out.printf("Transferred %s -> %s %s (commission: %s)", from, to, amount, commission); } void main() { //there is no way to see here which one is "from" which one is "to". Where is the amount, where is the commission? transfer(new Account(1), new Account(2), 100); transfer(new Account(2), new Account(1), 200, 5); } The lambda syntax allows reducing boiler-plate code significantly and new possibilities like named parameters and default parameter values. And it is syntactic sugar, just applying the same rules which are used for lambdas instead of usual method definitions. Like this: final class Account { private final int id; private String name; Account(int id) -> this.id = id; getId() -> id; getName() -> name; setName(String name) -> { this.name = name; return this; } toString() -> "Account{id=" + id + ", name='" + name + '\'' + '}'; equals(o) -> { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Account account = (Account) o; return id == account.id; } hashCode() -> Objects.hash(id); } interface TransferParams { //required params with compile-time guarantee Account from(); Account to(); int amount(); //optional param with mandatory default default commission() -> amount() / 20; // desugared "*default int commission() {return amount() / 10;}"* } void transfer(TransferParams p) { System.out.printf("Transferred %s -> %s %s (commission: %s)", p.from(), p.to(), p.amount(), p.commission()); } void main() { //this is very similar to single-method interface like @FunctionalInterface but for multiple methods hence it's using a "named" lambdas transfer({from() -> new Account(1); to() -> new Account(2); amount() -> 100 ;}); transfer({from() -> new Account(2); to() -> new Account(1); amount() -> 200; commission() -> 5;}); } //desugared would be something like that: *transfer(new TransferParams() { @Override public Account from() { return new Account(1); } @Override public Account to() { return new Account(2); } @Override public int amount() { return 100; }});* -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From steve at ethx.net Wed Jun 21 22:07:03 2023 From: steve at ethx.net (Steve Barham) Date: Wed, 21 Jun 2023 23:07:03 +0100 Subject: Allow lamba-like synax for methods In-Reply-To: References: Message-ID: Hi Anatoly, My feedback on your proposal would be to ask what you want, rather than to suggest how you might achieve what you want with (ab)use or extension of existing syntax. There have been prior discussions of ?concise method? declarations which would be worth reviewing, prior to your first suggestion. My understanding thus far is that the value returned from golfing away a line feed and brace is lacking. With respect to your second part; if named parameters and default parameter values (both goals I would love to see!) were implemented, I suspect it would be better for Java to support them through a clear and unambiguous form, rather than as a neat hack using existing syntax. My general perception from lurking is that syntax should come last in suggestions, not first. Cheers Steve * this is a very personal supposition, but I feel that named parameters and default values, if they are to come, have a substantial degree of overlap with deconstruction patterns, record like classes and reconstructors On Wed, 21 Jun 2023 at 22:40, Anatoly Kupriyanov wrote: > Not sure if it is the right mail list, please advise if there is a better > place to give suggestions about Java language. > > I suggest using lamba-like syntax for method implementation. Suppose you > have a code like this: > > final class Account { > private final int id; > private String name; > > public Account(int id) { > this.id = id; > } > > public int getId() { > return id; > } > > public String getName() { > return name; > } > > public Account setName(String name) { > this.name = name; > return this; > } > > @Override > public String toString() { > return "Account{id=" + id + ", name='" + name + '\'' + '}'; > } > > @Override > public boolean equals(Object o) { > if (this == o) return true; > if (o == null || getClass() != o.getClass()) return false; > Account account = (Account) o; > return id == account.id; > } > > @Override > public int hashCode() { > return Objects.hash(id); > } > } > > void transfer(Account from, Account to, int amount) { > transfer(from, to, amount, 0); > } > void transfer(Account from, Account to, int amount, int commission) { > System.out.printf("Transferred %s -> %s %s (commission: %s)", from, to, > amount, commission); > } > void main() { > //there is no way to see here which one is "from" which one is "to". > Where is the amount, where is the commission? > transfer(new Account(1), new Account(2), 100); > transfer(new Account(2), new Account(1), 200, 5); > } > > The lambda syntax allows reducing boiler-plate code significantly and new > possibilities like named parameters and default parameter values. And it is > syntactic sugar, just applying the same rules which are used for lambdas > instead of usual method definitions. Like this: > > final class Account { > private final int id; > private String name; > > Account(int id) -> this.id = id; > > getId() -> id; > > getName() -> name; > > setName(String name) -> { this.name = name; return this; } > > toString() -> "Account{id=" + id + ", name='" + name + '\'' + '}'; > > equals(o) -> { > if (this == o) return true; > if (o == null || getClass() != o.getClass()) return false; > Account account = (Account) o; > return id == account.id; > } > > hashCode() -> Objects.hash(id); > } > > interface TransferParams { > //required params with compile-time guarantee > Account from(); > Account to(); > int amount(); > //optional param with mandatory default > default commission() -> amount() / 20; > // desugared "*default int commission() {return amount() / 10;}"* > } > > void transfer(TransferParams p) { > System.out.printf("Transferred %s -> %s %s (commission: %s)", p.from(), > p.to(), p.amount(), p.commission()); > } > > void main() { > //this is very similar to single-method interface like @FunctionalInterface > but for multiple methods hence it's using a "named" lambdas > transfer({from() -> new Account(1); to() -> new Account(2); amount() -> > 100;}); > transfer({from() -> new Account(2); to() -> new Account(1); amount() -> > 200; commission() -> 5;}); > } > //desugared would be something like that: > > > > > > > > > > > > > *transfer(new TransferParams() { @Override public Account from() { return > new Account(1); } @Override public Account to() { return new Account(2); } > @Override public int amount() { return 100; }});* > > > > > -- > WBR, Anatoly. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jun 21 23:54:11 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 21 Jun 2023 19:54:11 -0400 Subject: Allow lamba-like synax for methods In-Reply-To: References: Message-ID: <722904d8-5c3f-e7bb-a9a5-a58a83b65f30@oracle.com> There's already a JEP draft on this: https://openjdk.org/jeps/8209434 But, it's low priority, needs a lot of work, and honestly, the "lambda form" (single expression form) is the less interesting part of the JEP.? Far more interesting is the part where you can delegate via method references. FWIW, what you've proposed here is not one feature, but at least four: ?- lambda-style method declaration (reasonable, but low priority) ?- inferred method return type (never going to happen, sorry) ?- inferred method parameter types (never going to happen, sorry) ?- a new object literal syntax On 6/21/2023 5:40 PM, Anatoly Kupriyanov wrote: > Not sure if it is the right mail list, please advise if there is a > better place to give suggestions about Java language. > > I suggest using lamba-like syntax for method implementation. Suppose > you have a code like this: > > final class Account { private final int id; private String name; > public Account(int id) { this.id = id; } public int getId() { return > id; } public String getName() { return name; } public > AccountsetName(String name) { this.name = name; > return this; } > @Override public String toString() { return "Account{id=" + id + ", > name='" + name + '\'' + '}'; } @Override public boolean equals(Object > o) { if (this == o) return true; if (o == null || getClass() != > o.getClass()) return false; Account account = (Account) o; return id > == account.id; } @Override public int hashCode() { return > Objects.hash(id); } } void transfer(Account from, Account to, int > amount) { transfer(from, to, amount, 0); } void transfer(Account from, > Account to, int amount, int commission) { > System.out.printf("Transferred %s -> %s %s (commission: %s)", from, > to, amount, commission); } > void main() { > //there is no way to see here which one is "from" which one is "to". > Where is the amount, where is the commission? > transfer(new Account(1), new Account(2), 100); transfer(new > Account(2), new Account(1), 200, 5); > } > The lambda syntax allows reducing boiler-plate code significantly and > new possibilities like named parameters and default parameter values. > And it is syntactic sugar, just applying the same rules which are used > for lambdas instead of usual method definitions. Like this: > > final class Account { private final int id; private String name; > Account(int id) -> this.id = id; getId() -> id; getName() -> name; > setName(String name) -> {this.name = name; return this; } toString() > -> "Account{id=" + id + ", name='" + name + '\'' + '}'; equals(o) -> { > if (this == o) return true; if (o == null || getClass() != > o.getClass()) return false; Account account = (Account) o; return id > == account.id; } > hashCode() -> Objects.hash(id); } > interface TransferParams { > //required params with compile-time guarantee > Account from(); Account to(); int amount(); > //optional param with mandatory default > default commission() -> amount() / 20; > // desugared "/default int commission() {return amount() / 10;}"/ > } > void transfer(TransferParams p) { System.out.printf("Transferred %s -> > %s %s (commission: %s)", p.from(), p.to (), p.amount(), > p.commission()); } > void main() { > //this is very similar to single-method interface like > @FunctionalInterface but for multiple methods hence it's using a > "named" lambdas > transfer({from() -> new Account(1); to() -> new Account(2); amount() > -> 100;}); transfer({from() -> new Account(2); to() -> new Account(1); > amount() -> 200; commission() -> 5;}); > } > //desugared would be something like that: > /transfer(new TransferParams() { @Override public Account from() { > return new Account(1); } @Override public Account to() { return new > Account(2); } @Override public int amount() { return 100; } });/ > > -- > WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Thu Jun 22 13:00:05 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 22 Jun 2023 09:00:05 -0400 Subject: Typo in the Project Amber page In-Reply-To: References: Message-ID: I have found the source of the bug and made a pull request here -- https://github.com/openjdk/amber-docs/pull/19 Here is a copy of the commit message explaining the bug. ~~~ Since "2020)" was on its own separate line, markdown interpreted it as a start of an ordered list. Just like this. 1) 2) 3) And then when that markdown got converted to HTML, it got converted into a literal
    tag, which stands for Ordered List - http://html5doctor.com/element-index/#ol However, in order for an Ordered List to show anything, it must have within it an
  1. tag, which stands for List Item. Since there is no such tag, HTML treats this as an empty list and shows nothing, resulting in the bug. Here is a snippet from the literal HTML from this link -- https://openjdk.org/projects/amber/
  2. Type Patterns in switch (September
    1. On Wed, Jun 21, 2023 at 3:12?PM David Alayachew wrote: > Hello Amber Dev Team, > > I was rereading through some of the design documents here, and it appears > that there is a typo. > > https://openjdk.org/projects/amber/ > > Ctrl+f "Type Patterns in switch" > > It says (September, but no year following after it. > > Thank you for your time and attention! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Thu Jun 22 13:06:03 2023 From: duke at openjdk.org (duke) Date: Thu, 22 Jun 2023 13:06:03 GMT Subject: git: openjdk/amber-docs: Update _index.md (#19) Message-ID: Changeset: 8ef99841 Author: David Alayachew Committer: GitHub Date: 2023-06-22 09:05:42 +0000 URL: https://git.openjdk.org/amber-docs/commit/8ef99841253cf0e6ffe532103fe9a46eae81a29b Update _index.md (#19) Since "2020)" was on its own separate line, markdown interpreted it as a start of an ordered list. Just like this. 1) 2) 3) And then when that markdown got converted to HTML, it got converted into a literal
        tag, which stands for Ordered List - http://html5doctor.com/element-index/#ol However, in order for an Ordered List to show anything, it must have within it an
      1. tag, which stands for List Item. Since there is no such tag, HTML treats this as an empty list and shows nothing, resulting in the bug. Here is a snippet from the literal HTML from this link -- https://openjdk.org/projects/amber/
      2. Type Patterns in switch (September
        1. ! site/_index.md From gavin.bierman at oracle.com Thu Jun 22 14:46:02 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 22 Jun 2023 14:46:02 +0000 Subject: Typo in the Project Amber page In-Reply-To: References: Message-ID: Thanks for spotting! I accepted the pull request. Gavin On 22 Jun 2023, at 14:00, David Alayachew wrote: I have found the source of the bug and made a pull request here -- https://github.com/openjdk/amber-docs/pull/19 Here is a copy of the commit message explaining the bug. ~~~ Since "2020)" was on its own separate line, markdown interpreted it as a start of an ordered list. Just like this. 1) 2) 3) And then when that markdown got converted to HTML, it got converted into a literal
            tag, which stands for Ordered List - http://html5doctor.com/element-index/#ol However, in order for an Ordered List to show anything, it must have within it an
          1. tag, which stands for List Item. Since there is no such tag, HTML treats this as an empty list and shows nothing, resulting in the bug. Here is a snippet from the literal HTML from this link -- https://openjdk.org/projects/amber/
          2. Type Patterns in switch (September
            1. On Wed, Jun 21, 2023 at 3:12?PM David Alayachew > wrote: Hello Amber Dev Team, I was rereading through some of the design documents here, and it appears that there is a typo. https://openjdk.org/projects/amber/ Ctrl+f "Type Patterns in switch" It says (September, but no year following after it. Thank you for your time and attention! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From ian at darwinsys.com Wed Jun 28 21:10:25 2023 From: ian at darwinsys.com (Ian Darwin) Date: Wed, 28 Jun 2023 17:10:25 -0400 Subject: JEP 445 Message-ID: <43c772d3-99c7-febf-9622-7f1827edb1e4@darwinsys.com> Looks great! I wish I'd thought of it. Heck, I wish James & the early team had thought of it. Would have made teaching so much easier over the years. From davidalayachew at gmail.com Fri Jun 30 22:28:46 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 30 Jun 2023 18:28:46 -0400 Subject: Question about circular references Message-ID: Hello all, First off, please let me know if I have CC'd the wrong groups. I've CC'd the Amber Dev Team since this involves records, but it's not specifically about records. --- For the past couple of weeks, I have been building a program that uses a State Transition Diagram (aka State Machine, Finite Automata, etc. -- https://en.wikipedia.org/wiki/Finite-state_machine) to model part of its control flow. I've been using some Amber features to facilitate this (and having a wonderful time of it), but then I hit a snag. Here is a(n EXTREMELY) simplified version of my actual problem. Imagine I have code like the following. ```java sealed interface Node permits StartNode, BranchingNode, EndNode {...unrelated stuff here...} record StartNode (Node a, Node b, Node c) implements Node {} record BranchingNode (Node a, Node b, Node c, ...fields unrelated to transitioning...) implements Node {} record EndNode (...fields unrelated to transitioning...) implements Node {} ``` This type hierarchy is meant to represent a control flow of sorts. Control flow is (imo) best modeled using a State Transition Diagram, so I instinctively reached for that. And since my API needed to be nothing but the data (each Node needed to be tightly coupled to my internal state representation), I realized that this is an ideal use case for records. Things worked out well enough until I tried to model a circular relationship. Through chance, all of my control flows up to this point were tree-like, so I could model them by starting from the "leaves," then climbing up until I got to the "roots". To use State Transition Diagram terminology, I started from my exit states and modeled my way up to my entry states. For example, assume that my State Transition Diagram is as so. S ---a---> T S ---b---> U S ---c---> V T ---a---> U T ---b---> V T ---c---> E U ---a---> V U --b|c--> E V -a|b|c-> E S is my StartNode, and E is my ExitNode. In this case, modeling with records is easy. It would look like so. ```java ExitNode e = new ExitNode<>(...unrelated...); BranchingNode v = new BranchingNode<>(e, e, e, ...unrelated...); BranchingNode u = new BranchingNode<>(v, e, e, ...unrelated...); BranchingNode t = new BranchingNode<>(u, v, e, ...unrelated...); StartNode s = new StartNode<>(t, u, v); return s; ``` But once I hit a circular reference, I could not figure out how to model the code using the same format. For example, what if I say the following instead? V ---a---> T How do I model that using my current representation? Obviously, I could change my representation, but all of them required me to "taint" my representation in incorrect ways. For example, I could swap out my records for simple classes where the references to Node's were mutable. But I strongly disapprove of this strategy because these nodes do NOT have a mutable relationship. Obviously, I could put something in the Javadoc, but I want to fix the incorrect representation, not put a warning sign in front of it. Also, I could use indirection, like having a separate Map whose values are the actual Node references and the keys would be a record Pair(String nodeId, Branch branch) {} where Branch is enum Branch { a, b, c, ; } and then give each Node an id, changing my record to now be record BranchingNode (String id, ...the same as above...) {}. But ignoring the fact that I now have to deal with managing an id, I've also added a lot of unnecessary bloat and indirection just to get the circular reference I wanted. What should be a direct relationship now requires a Map lookup. In that same vein, someone suggested that I use pattern-matching for switch, but that would require me to create a new switch expression for every single state. That's even more verbose and indirect than the Map. At least with the map, I can put them all in one expression. This strategy has an expression for each state! I've been told that there is another pathway involving reflection, but it basically amounts to breaking the rules of Java. Apparently, you can turn off finality to insert in fields you want, and then turn it back on? I liked this idea the least compared to all of the others, so I didn't pursue it any further. In the end, I decided to go down the Map lookup route. But I just wanted to point out my experience with this because it was a surprising and annoying speed bump along an otherwise smooth road. I didn't think that something as small as a circular reference would require me to uproot my entire solution. And finally, I want to emphasize that the same blockers above apply no matter what pathway I go down. I had actually tried implementing this first as an enum before I tried a record, since an enum would more accurately represent my state. ```java enum State { V(T, EXIT, EXIT), //FAILURE -- T cannot be referenced yet U(V, EXIT, EXIT), T(U, V, EXIT), ; ...public final fields and constructor... } ``` But even then, the same problem occurred -- I can't reference an enum value until it has been declared. I thought going down the path of records would give me the flexibility I wanted, but no dice. It reminded me of that one programming meme. > * High Quality > * Quickly Built > * Low Cost > > You can only pick 2 But instead, it's * Circular Relationship * Immutability * Direct References What are your thoughts? Is this a problem in your eyes too? Or simply a non-issue? Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: