From gavin.bierman at oracle.com Fri Nov 1 16:04:28 2024 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 1 Nov 2024 16:04:28 +0000 Subject: New candidate JEP: 492: Flexible Constructor Bodies (Third Preview) In-Reply-To: <20241002131430.EBCC577CE34@eggemoggin.niobe.net> References: <20241002131430.EBCC577CE34@eggemoggin.niobe.net> Message-ID: <2C1DE5FA-44F5-4D80-81F2-90BF21574EC4@oracle.com> Dear experts: The first draft of a spec covering JEP 492: Flexible Constructor Bodies (Third Preview) is available: https://cr.openjdk.org/~gbierman/jep492/latest/ (As the feature is unchanged from the second preview, this spec is the same as the previous edition but with a small number of minor bugfixes.) Feel free to contact me directly or on this list with any comments. Thanks Gavin On 2 Oct 2024, at 14:14, Mark Reinhold wrote: https://openjdk.org/jeps/492 Summary: In constructors in the Java programming language, allow statements to appear before an explicit constructor invocation, i.e., super(..) or this(..). The statements cannot reference the instance under construction, but they can initialize its fields. Initializing fields before invoking another constructor makes a class more reliable when methods are overridden. This is a preview language feature. - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Nov 4 11:29:31 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 4 Nov 2024 11:29:31 +0000 Subject: New candidate JEP: 492: Flexible Constructor Bodies (Third Preview) In-Reply-To: <2C1DE5FA-44F5-4D80-81F2-90BF21574EC4@oracle.com> References: <20241002131430.EBCC577CE34@eggemoggin.niobe.net> <2C1DE5FA-44F5-4D80-81F2-90BF21574EC4@oracle.com> Message-ID: Hi Gavin, the changes look good, thanks! Some minor comments below: * "If a constructor body does not begin with contain an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its the direct superclass that takes no arguments." Should we use the newly introduced terminology here (to make things extra clear)? E.g. say that if the constructor does not contain an explicit constructor invocation, then all statements are implicitly treated as epilogue, there's no prologue, and the explicit constructor is "super()". * "It is a compile-time error for a constructor to directly or indirectly invoke itself through a series of one or more alternate constructor invocations." I found this confusing -- after all constructor of A can do "new B()" which can then do "new A()". Does the constructor invoke itself? I think you mean "invoke itself _on the same receiver object_" ? * The fixes to 8.8.7.1 and 15.9.2 look good! (the PR for them is in [1]) Maurizio On 01/11/2024 16:04, Gavin Bierman wrote: > Dear experts: > > The first draft of a spec covering JEP 492: Flexible Constructor > Bodies (Third Preview) is available: > > https://cr.openjdk.org/~gbierman/jep492/latest/ > > (As the feature is unchanged from the second preview, this spec is the > same as the previous edition but with a small number of minor bugfixes.) > > Feel free to contact me directly or on this list with any comments. > > Thanks > Gavin > > >> On 2 Oct 2024, at 14:14, Mark Reinhold wrote: >> >> https://openjdk.org/jeps/492 >> >> ?Summary: In constructors in the Java programming language, allow >> ?statements to appear before an explicit constructor invocation, i.e., >> ?super(..) or this(..). ?The statements cannot reference the instance >> ?under construction, but they can initialize its fields. ?Initializing >> ?fields before invoking another constructor makes a class more reliable >> ?when methods are overridden. ?This is a preview language feature. >> >> - Mark > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Mon Nov 4 12:23:39 2024 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 4 Nov 2024 12:23:39 +0000 Subject: New candidate JEP: 492: Flexible Constructor Bodies (Third Preview) In-Reply-To: References: <20241002131430.EBCC577CE34@eggemoggin.niobe.net> <2C1DE5FA-44F5-4D80-81F2-90BF21574EC4@oracle.com> Message-ID: <56E680CD-C7E8-4516-A21C-DBF2030BBC0A@oracle.com> Thanks Maurizio. On 4 Nov 2024, at 11:29, Maurizio Cimadamore wrote: Hi Gavin, the changes look good, thanks! Some minor comments below: * "If a constructor body does not begin with contain an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its the direct superclass that takes no arguments." Should we use the newly introduced terminology here (to make things extra clear)? E.g. say that if the constructor does not contain an explicit constructor invocation, then all statements are implicitly treated as epilogue, there's no prologue, and the explicit constructor is "super()". Thanks! Yes we should do this. I have updated the spec. * "It is a compile-time error for a constructor to directly or indirectly invoke itself through a series of one or more alternate constructor invocations." I found this confusing -- after all constructor of A can do "new B()" which can then do "new A()". Does the constructor invoke itself? I think you mean "invoke itself _on the same receiver object_" ? This is actually text that got moved from 8.8.7.1; so dates from prehistory :-) I think it is okay. It means that you can?t do a this( ?) [that?s invoking a constructor] that is actually calling yourself; or if you call another constructor, then you can?t have a chain of this invocations that end up calling you. A new expression isn?t considered an invocation. * The fixes to 8.8.7.1 and 15.9.2 look good! (the PR for them is in [1]) Yay. Thanks, Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From badalov.turxan at gmail.com Wed Nov 6 16:59:44 2024 From: badalov.turxan at gmail.com (Turkhan Badalov) Date: Wed, 6 Nov 2024 20:59:44 +0400 Subject: Fully qualified enum constants in switch labels since Java 21 Message-ID: The JLS says "Every case constant must be either a constant expression (?15.29), or the name of an enum constant (?8.9.1), otherwise a compile-time error occurs." which I believe explains why using anything besides simple name of an enum constant in switch labels leads to compilation error. In fact, this was very obvious prior to java 14 where SwitchLabel expected EnumConstantName which itself was just an Identifier: https://docs.oracle.com/javase/specs/jls/se13/preview/switch-expressions.html#jep354-14.11.1 . Today, even though SwitchLabel expects a ConditionalExpression which many layers down is ExpressionName, the restriction remained until Java 21. Since Java 21 using fully-qualified enum names in switch labels compiles and works fine. Yet I can't find anything in JLS which would result in this change. Is that a bug or intended to be so? Will appreciate if someone can point me to the part of JLS that resulted in this change. Attaching a minimal reproducible code which fails on Java versions prior to 21 and compiles since 21. package org.example; enum Demo { ONE, TWO } public class Main { public static void main(String[] args) { final Demo input = Demo.ONE; switch(input) { case Demo.ONE: {} case org.example.Demo.TWO: {} } } } Regards, Turkhan -------------- next part -------------- An HTML attachment was scrubbed... URL: From alex.buckley at oracle.com Wed Nov 6 19:54:27 2024 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 6 Nov 2024 11:54:27 -0800 Subject: Fully qualified enum constants in switch labels since Java 21 In-Reply-To: References: Message-ID: <4251e505-8754-423a-8405-3fac49c51384@oracle.com> On 11/6/2024 8:59 AM, Turkhan Badalov wrote: > Since Java 21 using fully-qualified enum names in switch labels compiles > and works fine. Yet I can't find anything in JLS which would result in > this change. Is that a bug or intended to be so? Allowing qualified enum constants as case constants was indeed part of Java 21. It came in via JEP 441: https://openjdk.org/jeps/441#History I recommend looking at the evolution of JLS 14.11.1 in Gavin's spec drafts for JEP 441: https://cr.openjdk.org/~gbierman/jep440%2B441/ The drafts initially spelled out that "the (simple or qualified) name of an enum constant" is allowed, but later minimized change by reusing the phrase from JLS 20: "the name of an enum constant". That phrase is accurate: it allows all the names expressible in the grammar, which was changed previously to use ConditionalExpression and thus allows qualified as well as simple names. Alex From me at adamgent.com Thu Nov 7 14:28:32 2024 From: me at adamgent.com (Adam Gent) Date: Thu, 07 Nov 2024 09:28:32 -0500 Subject: Should JEP 468 derived withers be opt in? Message-ID: One concern I have with "Withers" is that they don't codify the need or requirement of multiple fields to be set at the exact time. Currently folks are probably making static methods on records for creation. After (ab)using records in our code base that is less academic and more business we frequently have a time field. record Something(String name, Instant updateTime, ... other fields) { static Something withName(String name, Instant updateTime) { ... } // Other with methods always including updateTime. } I admit my example is poor but the idea is that we want to encodify that the time needs to be set on each call. If JEP 468 comes through one could create the record without specifying the time field. Basically withers allow each field to be updated independently. That is JEP 468 sort of increases the API of existing code. So I wonder if a safer backward compatibility is to provide some sort of marker annotation? e.g. `@Withers` placed on the record or some other way to mark that the record is safe to have withers. Thoughts? Cheers -Adam From me at adamgent.com Thu Nov 7 14:49:56 2024 From: me at adamgent.com (Adam Gent) Date: Thu, 07 Nov 2024 09:49:56 -0500 Subject: Should JEP 468 derived withers be opt in? In-Reply-To: References: Message-ID: <561b42dc-c819-414c-88b4-fbd204254178@app.fastmail.com> Apologies. I meant instance factory methods and not static methods on my previous message. On Thu, Nov 7, 2024, at 9:28 AM, Adam Gent wrote: > One concern I have with "Withers" is that they don't codify the need or > requirement of multiple fields to be set at the exact time. > > Currently folks are probably making static methods on records for > creation. After (ab)using records in our code base that is less > academic and more business we frequently have a time field. > > record Something(String name, Instant updateTime, ... other fields) { > static Something withName(String name, Instant updateTime) { > ... > } > // Other with methods always including updateTime. > } > > I admit my example is poor but the idea is that we want to encodify > that the time needs to be set on each call. > > If JEP 468 comes through one could create the record without specifying > the time field. Basically withers allow each field to be updated > independently. That is JEP 468 sort of increases the API of existing > code. > > So I wonder if a safer backward compatibility is to provide some sort > of marker annotation? e.g. `@Withers` placed on the record or some > other way to mark that the record is safe to have withers. > > Thoughts? > > Cheers > -Adam From brian.goetz at oracle.com Thu Nov 7 15:57:39 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 7 Nov 2024 10:57:39 -0500 Subject: Should JEP 468 derived withers be opt in? In-Reply-To: References: Message-ID: <30c1aee1-b8e5-4374-8463-96d2d918a8ea@oracle.com> Record constructors are public and can (theoretically) set all the fields.? So if you have a record with invariants (such as "name != null" or "x < y") these should be checked in the constructor. If they are checked in the constructor, they will also be checked implicitly in a `with` expression, since `with` expressions go back through the constructor to produce the result. It seems that in your example, the requirement you want to impose is "when I call withName on a Something, I must update the time too." But even without withers, your program doesn't enforce that: ? ? Something s = new Something("Bob", randomTime()); ? ? Something ss = s.withName("Mary", s.updateTime()); So while ss is derived from s, they share the same update time.? The requirement you are trying to enforce is a "handshake" one: "when you update something, set the update time."? And if handshake enforcement is enough without withers, it is enough with withers too: ??? ss = s with { name = "Mary"; updateTime = now(); } On 11/7/2024 9:28 AM, Adam Gent wrote: > One concern I have with "Withers" is that they don't codify the need or requirement of multiple fields to be set at the exact time. > > Currently folks are probably making static methods on records for creation. After (ab)using records in our code base that is less academic and more business we frequently have a time field. > > record Something(String name, Instant updateTime, ... other fields) { > static Something withName(String name, Instant updateTime) { > ... > } > // Other with methods always including updateTime. > } > > I admit my example is poor but the idea is that we want to encodify that the time needs to be set on each call. > > If JEP 468 comes through one could create the record without specifying the time field. Basically withers allow each field to be updated independently. That is JEP 468 sort of increases the API of existing code. > > So I wonder if a safer backward compatibility is to provide some sort of marker annotation? e.g. `@Withers` placed on the record or some other way to mark that the record is safe to have withers. > > Thoughts? > > Cheers > -Adam From me at adamgent.com Thu Nov 7 16:50:14 2024 From: me at adamgent.com (Adam Gent) Date: Thu, 07 Nov 2024 11:50:14 -0500 Subject: Should JEP 468 derived withers be opt in? In-Reply-To: <30c1aee1-b8e5-4374-8463-96d2d918a8ea@oracle.com> References: <30c1aee1-b8e5-4374-8463-96d2d918a8ea@oracle.com> Message-ID: <40e24a69-dd98-42b0-b51f-38e3cc6ec57f@app.fastmail.com> I generally agree and I'm hoping to eliminate this concern (I was hesitant to even bring it up) but the current manual `withers` do differ then proposed withers in that they have access to the previous instance (that is withName with the time could check that the time is newer than "this"). While yes records do provide public construction with all fields (and thus what you can do with that is the same as withers) but the idea is if you are creating the initial record you have more information to create it correctly than perhaps downstream where one just wants to update/transform a few fields. The thing is records I assume (and probably falsely) are akin to ML / Haskell style records. In those languages there is no behavior in the construction. They are pure data so validation needs to happen elsewhere. Thus pure records are rarely API and are encapsulated. Java records do have the validation in the constructor and thus are more likely to be public API. I agree though this all pretty weak arguments. And obviously would prefer Java with withers then this minor problem of "trying" to force users to use certain methods of construction. I just wanted to give feedback on record usage currently and how I'm surprised at the sheer number of "wither" like methods we have that take multiple arguments but that could be largely the pain of adding withers. I shall look more into our newer code base later this week. Thanks for the reply! -Adam On Thu, Nov 7, 2024, at 10:57 AM, Brian Goetz wrote: > Record constructors are public and can (theoretically) set all the > fields.? So if you have a record with invariants (such as "name != null" > or "x < y") these should be checked in the constructor. > > If they are checked in the constructor, they will also be checked > implicitly in a `with` expression, since `with` expressions go back > through the constructor to produce the result. > > It seems that in your example, the requirement you want to impose is > "when I call withName on a Something, I must update the time too." But > even without withers, your program doesn't enforce that: > > ? ? Something s = new Something("Bob", randomTime()); > ? ? Something ss = s.withName("Mary", s.updateTime()); > > So while ss is derived from s, they share the same update time.? The > requirement you are trying to enforce is a "handshake" one: "when you > update something, set the update time."? And if handshake enforcement is > enough without withers, it is enough with withers too: > > ??? ss = s with { name = "Mary"; updateTime = now(); } > > > > > On 11/7/2024 9:28 AM, Adam Gent wrote: >> One concern I have with "Withers" is that they don't codify the need or requirement of multiple fields to be set at the exact time. >> >> Currently folks are probably making static methods on records for creation. After (ab)using records in our code base that is less academic and more business we frequently have a time field. >> >> record Something(String name, Instant updateTime, ... other fields) { >> static Something withName(String name, Instant updateTime) { >> ... >> } >> // Other with methods always including updateTime. >> } >> >> I admit my example is poor but the idea is that we want to encodify that the time needs to be set on each call. >> >> If JEP 468 comes through one could create the record without specifying the time field. Basically withers allow each field to be updated independently. That is JEP 468 sort of increases the API of existing code. >> >> So I wonder if a safer backward compatibility is to provide some sort of marker annotation? e.g. `@Withers` placed on the record or some other way to mark that the record is safe to have withers. >> >> Thoughts? >> >> Cheers >> -Adam From brian.goetz at oracle.com Thu Nov 7 16:59:46 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 7 Nov 2024 11:59:46 -0500 Subject: Should JEP 468 derived withers be opt in? In-Reply-To: <40e24a69-dd98-42b0-b51f-38e3cc6ec57f@app.fastmail.com> References: <30c1aee1-b8e5-4374-8463-96d2d918a8ea@oracle.com> <40e24a69-dd98-42b0-b51f-38e3cc6ec57f@app.fastmail.com> Message-ID: <972da7c9-ba0a-4552-b8cc-8578d7f95ca6@oracle.com> You should think of a wither as a shorthand for something you could have written without withers: ??? a with { S*; } means ??? switch (a) { ?????? case R(V) -> { S*; yield new R(V); } ??? } where V is the state vector (names and types) of R, and S* are statements that mutate the names in V.? There's no magic here. On 11/7/2024 11:50 AM, Adam Gent wrote: > I generally agree and I'm hoping to eliminate this concern (I was hesitant to even bring it up) but the current manual `withers` do differ then proposed withers in that they have access to the previous instance (that is withName with the time could check that the time is newer than "this"). > > While yes records do provide public construction with all fields (and thus what you can do with that is the same as withers) but the idea is if you are creating the initial record you have more information to create it correctly than perhaps downstream where one just wants to update/transform a few fields. > > The thing is records I assume (and probably falsely) are akin to ML / Haskell style records. In those languages there is no behavior in the construction. They are pure data so validation needs to happen elsewhere. Thus pure records are rarely API and are encapsulated. Java records do have the validation in the constructor and thus are more likely to be public API. > > I agree though this all pretty weak arguments. And obviously would prefer Java with withers then this minor problem of "trying" to force users to use certain methods of construction. I just wanted to give feedback on record usage currently and how I'm surprised at the sheer number of "wither" like methods we have that take multiple arguments but that could be largely the pain of adding withers. I shall look more into our newer code base later this week. > > Thanks for the reply! > > -Adam > > On Thu, Nov 7, 2024, at 10:57 AM, Brian Goetz wrote: >> Record constructors are public and can (theoretically) set all the >> fields.? So if you have a record with invariants (such as "name != null" >> or "x < y") these should be checked in the constructor. >> >> If they are checked in the constructor, they will also be checked >> implicitly in a `with` expression, since `with` expressions go back >> through the constructor to produce the result. >> >> It seems that in your example, the requirement you want to impose is >> "when I call withName on a Something, I must update the time too." But >> even without withers, your program doesn't enforce that: >> >> ? ? Something s = new Something("Bob", randomTime()); >> ? ? Something ss = s.withName("Mary", s.updateTime()); >> >> So while ss is derived from s, they share the same update time.? The >> requirement you are trying to enforce is a "handshake" one: "when you >> update something, set the update time."? And if handshake enforcement is >> enough without withers, it is enough with withers too: >> >> ??? ss = s with { name = "Mary"; updateTime = now(); } >> >> >> >> >> On 11/7/2024 9:28 AM, Adam Gent wrote: >>> One concern I have with "Withers" is that they don't codify the need or requirement of multiple fields to be set at the exact time. >>> >>> Currently folks are probably making static methods on records for creation. After (ab)using records in our code base that is less academic and more business we frequently have a time field. >>> >>> record Something(String name, Instant updateTime, ... other fields) { >>> static Something withName(String name, Instant updateTime) { >>> ... >>> } >>> // Other with methods always including updateTime. >>> } >>> >>> I admit my example is poor but the idea is that we want to encodify that the time needs to be set on each call. >>> >>> If JEP 468 comes through one could create the record without specifying the time field. Basically withers allow each field to be updated independently. That is JEP 468 sort of increases the API of existing code. >>> >>> So I wonder if a safer backward compatibility is to provide some sort of marker annotation? e.g. `@Withers` placed on the record or some other way to mark that the record is safe to have withers. >>> >>> Thoughts? >>> >>> Cheers >>> -Adam -------------- next part -------------- An HTML attachment was scrubbed... URL: From bburd at drew.edu Thu Nov 7 17:06:39 2024 From: bburd at drew.edu (Barry Burd) Date: Thu, 7 Nov 2024 12:06:39 -0500 Subject: JEP 495 Question Message-ID: I see that parameterless println() and readln() methods are being added to java.io.IO in JEP 495. I'm still wondering if there's been any thought to including methods like readInt and readDouble. In the early stage of an introduction to Java, it's common to have the student input int values and double values for simple examples. With the current spec, the best way to do this (as far as I can see) is ????? var n = Integer.parseInt(readln("Enter a number")); It seems natural to simplify this with a readInt method, and the addition of such a method wouldn't weigh heavily on the java.io.IO package. Any thoughts? ? --Barry From me at adamgent.com Thu Nov 7 17:24:16 2024 From: me at adamgent.com (Adam Gent) Date: Thu, 07 Nov 2024 12:24:16 -0500 Subject: Should JEP 468 derived withers be opt in? In-Reply-To: <972da7c9-ba0a-4552-b8cc-8578d7f95ca6@oracle.com> References: <30c1aee1-b8e5-4374-8463-96d2d918a8ea@oracle.com> <40e24a69-dd98-42b0-b51f-38e3cc6ec57f@app.fastmail.com> <972da7c9-ba0a-4552-b8cc-8578d7f95ca6@oracle.com> Message-ID: <6c30705c-ef21-4453-ba54-65a290b7acd6@app.fastmail.com> I'm fully convinced now especially because of the pattern matching. I have been wanting to kill that concern for some time and I had other arguments but the pattern matching part really sells it. I'm embarrassed I didn't come to that conclusion earlier. -Adam On Thu, Nov 7, 2024, at 11:59 AM, Brian Goetz wrote: > You should think of a wither as a shorthand for something you could have written without withers: > > a with { S*; } > > means > > switch (a) { > case R(V) -> { S*; yield new R(V); } > } > > where V is the state vector (names and types) of R, and S* are statements that mutate the names in V. There's no magic here. > > On 11/7/2024 11:50 AM, Adam Gent wrote: >> I generally agree and I'm hoping to eliminate this concern (I was hesitant to even bring it up) but the current manual `withers` do differ then proposed withers in that they have access to the previous instance (that is withName with the time could check that the time is newer than "this"). >> >> While yes records do provide public construction with all fields (and thus what you can do with that is the same as withers) but the idea is if you are creating the initial record you have more information to create it correctly than perhaps downstream where one just wants to update/transform a few fields. >> >> The thing is records I assume (and probably falsely) are akin to ML / Haskell style records. In those languages there is no behavior in the construction. They are pure data so validation needs to happen elsewhere. Thus pure records are rarely API and are encapsulated. Java records do have the validation in the constructor and thus are more likely to be public API. >> >> I agree though this all pretty weak arguments. And obviously would prefer Java with withers then this minor problem of "trying" to force users to use certain methods of construction. I just wanted to give feedback on record usage currently and how I'm surprised at the sheer number of "wither" like methods we have that take multiple arguments but that could be largely the pain of adding withers. I shall look more into our newer code base later this week. >> >> Thanks for the reply! >> >> -Adam >> >> On Thu, Nov 7, 2024, at 10:57 AM, Brian Goetz wrote: >> >>> Record constructors are public and can (theoretically) set all the >>> fields. So if you have a record with invariants (such as "name != null" >>> or "x < y") these should be checked in the constructor. >>> >>> If they are checked in the constructor, they will also be checked >>> implicitly in a `with` expression, since `with` expressions go back >>> through the constructor to produce the result. >>> >>> It seems that in your example, the requirement you want to impose is >>> "when I call withName on a Something, I must update the time too." But >>> even without withers, your program doesn't enforce that: >>> >>> Something s = new Something("Bob", randomTime()); >>> Something ss = s.withName("Mary", s.updateTime()); >>> >>> So while ss is derived from s, they share the same update time. The >>> requirement you are trying to enforce is a "handshake" one: "when you >>> update something, set the update time." And if handshake enforcement is >>> enough without withers, it is enough with withers too: >>> >>> ss = s with { name = "Mary"; updateTime = now(); } >>> >>> >>> >>> >>> On 11/7/2024 9:28 AM, Adam Gent wrote: >>> >>>> One concern I have with "Withers" is that they don't codify the need or requirement of multiple fields to be set at the exact time. >>>> >>>> Currently folks are probably making static methods on records for creation. After (ab)using records in our code base that is less academic and more business we frequently have a time field. >>>> >>>> record Something(String name, Instant updateTime, ... other fields) { >>>> static Something withName(String name, Instant updateTime) { >>>> ... >>>> } >>>> // Other with methods always including updateTime. >>>> } >>>> >>>> I admit my example is poor but the idea is that we want to encodify that the time needs to be set on each call. >>>> >>>> If JEP 468 comes through one could create the record without specifying the time field. Basically withers allow each field to be updated independently. That is JEP 468 sort of increases the API of existing code. >>>> >>>> So I wonder if a safer backward compatibility is to provide some sort of marker annotation? e.g. `@Withers` placed on the record or some other way to mark that the record is safe to have withers. >>>> >>>> Thoughts? >>>> >>>> Cheers >>>> -Adam >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Thu Nov 7 18:23:32 2024 From: ethan at mccue.dev (Ethan McCue) Date: Thu, 7 Nov 2024 13:23:32 -0500 Subject: JEP 495 Question In-Reply-To: References: Message-ID: CC-ing Mr. Fogel, who also just made an email about this on discuss. I will forward that thread to you Mr. Burd - though this is the proper place to continue discussion. On Thu, Nov 7, 2024 at 12:45?PM Barry Burd wrote: > I see that parameterless println() and readln() methods are being added > to java.io.IO in JEP 495. I'm still wondering if there's been any > thought to including methods like readInt and readDouble. In the early > stage of an introduction to Java, it's common to have the student input > int values and double values for simple examples. With the current spec, > the best way to do this (as far as I can see) is > > var n = Integer.parseInt(readln("Enter a number")); > > It seems natural to simplify this with a readInt method, and the > addition of such a method wouldn't weigh heavily on the java.io.IO > package. > Any thoughts? > --Barry > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Thu Nov 7 18:45:08 2024 From: ron.pressler at oracle.com (Ron Pressler) Date: Thu, 7 Nov 2024 18:45:08 +0000 Subject: JEP 495 Question In-Reply-To: References: Message-ID: This has been considered, and the decision, for the time being, is to err on the side of caution and not add such methods. While readln and println can be clearly described and happily used by both beginners and veterans writing scripts, this is not quite the case for methods for parsing numbers read as input. For example, an implementation that does `return Integer.parseInt(readln(prompt))` is not that useful for veterans writing scripts as they may just as reasonably want to read, say, a CSV file, and parse multiple numbers per line. It is also not quite intuitive for beginners, either, who may expect the console line-editing functionality to prevent entering anything that isn?t an integer (or a floating-point number). So such operations ? however they?re defined ? serve as bad primitives. While there?s no doubt that `Integer.parseInt(readln("Enter a number?))` is more characters for a beginner to type than `readlnInt(?Enter a number?)` it more clearly communicates that what is read is an arbitrary string that the program then attempts to parse as an integer, and that the latter operation may fail independently from the former. So rather than adding convenience methods that perform some specific non-primitive operations (begging requests for adding others still) it?s best to wait a while and see what the most urgent issues encountered are in this new world before offering solutions for what may or may not be significant issues. ? Ron > On 7 Nov 2024, at 17:06, Barry Burd wrote: > > I see that parameterless println() and readln() methods are being added to java.io.IO in JEP 495. I'm still wondering if there's been any thought to including methods like readInt and readDouble. In the early stage of an introduction to Java, it's common to have the student input int values and double values for simple examples. With the current spec, the best way to do this (as far as I can see) is > > var n = Integer.parseInt(readln("Enter a number")); > > It seems natural to simplify this with a readInt method, and the addition of such a method wouldn't weigh heavily on the java.io.IO package. > Any thoughts? > --Barry From kfogel at dawsoncollege.qc.ca Thu Nov 7 19:16:40 2024 From: kfogel at dawsoncollege.qc.ca (Kenneth Fogel) Date: Thu, 7 Nov 2024 19:16:40 +0000 Subject: How about a readln for numbers Message-ID: The readln method introduced as part java.base is great because it includes the prompt for the input. The only shortcoming is that if the input must be a number then you still need to employ a static class member such as Double.parseDouble() to make it a number: // Currently var loan = Double.parseDouble(readln("Loan: ")); // My delusional idea var loan = readDbl("Loan: "); I can think of reasons why this could be a bad idea. I taught my students to use a construct using Scanner to create a console input that passed my Beethoven test (while humming Beethoven?s 5th symphony randomly strike keys on your keyboard as if you were playing the symphony and your code should just report invalid input and not an exception). If we had a readInt and/or readDbl they would not pass this test. That Python would happily accept anything and then fail when the value was used in a calculation is no better. But, for learning Java could we have a readInt or readDbl alongside readln? I know that DataInputStream has such methods, but without a prompt and it must be attached to an input stream such as a file. As always, just thinking out loud. Feel free to use my name in vain. Ken This message and a back and forth with Ethan McCue began in the OpenJDK Discus list and we were asked to move it here. Here is the summary of messages in the order they were sent to the list. The message above is the last one. void main() { int number; do { String input = readln("Enter a number between 1 and 10: "); try { number = Integer.parseInt(input); if (number < 1 || number > 10) { println("Number out of range"); } // All is well break; } catch (NumberFormatException _) { println("You have not entered a number"); } } while (true); println(number); } For color on how that might affect the pedagogy: "First we need to get input from the user, this is a String which represents text" void main() { String input = readln("Enter a number between 1 and 10: "); } "We can turn a String into an int by writing Integer.parseInt(). If what they wrote isn't a number it will crash" void main() { String input = readln("Enter a number between 1 and 10: "); int number = Integer.parseInt(input); } "To handle crashes we can put the code that might crash inside of try { }, then write the way it might crash in a catch clause" void main() { String input = readln("Enter a number between 1 and 10: "); try { int number = Integer.parseInt(input); println("You gave: " + number); } catch (NumberFormatException e) { println("Bad number: " + input); } } "If they gave a number that wasn't between 1 and 10 ..." void main() { String input = readln("Enter a number between 1 and 10: "); try { int number = Integer.parseInt(input); if (number < 1 || number > 10) { println("Number is not between 1 and 10: " + number); } else { println("You gave: " + number); } } catch (NumberFormatException e) { println("Bad number: " + input); } } "Now we want to keep asking them until they give a valid input. To do this, first delay the assignment of number" void main() { String input = readln("Enter a number between 1 and 10: "); int number; try { number = Integer.parseInt(input); if (number < 1 || number > 10) { println("Number is not between 1 and 10: " + number); } else { println("You gave: " + number); } } catch (NumberFormatException e) { println("Bad number: " + input); } } ... and so on until you reach void main() { String input = readln("Enter a number between 1 and 10: "); int number; do { try { number = Integer.parseInt(input); if (number < 1 || number > 10) { println("Number is not between 1 and 10: " + number); } else { break; } } catch (NumberFormatException e) { println("Bad number: " + input); } } while (true); println("You gave: " + number); } On Thu, Nov 7, 2024 at 12:50?PM Ethan McCue > wrote: So with all that context, do you see why accepting Double.parseDouble(readln()); might be worth it? "Paving the onramp" shouldn't mean only "paving the first 48 hours." The convenience afforded by a dedicated readDouble, at least to me, feels outweighed by * The loss of a perfectly good opportunity to explain the fundamentals of parsing (strings represent text, you can interpret that text by....) * The loss of a perfectly good opportunity to teach basic exception handling * The divergence with the behavior of Scanner * The combinatorial explosion of "why not readByte?" * The privileged position it puts primitives in as the end-point of interpreting user input * Guns, Feet (I've separately voiced my concerns about readln in this regard.) On Thu, Nov 7, 2024 at 12:40?PM Kenneth Fogel > wrote: Here is an example of a Beethoven test passing routine that expects a number for 1 to 10. The Scanner, sc, has already been initialized. I also taught how to use regular expressions to fine tune what is acceptable input and the acceptable range such that you only need to use readLine so that subsequent input is not messed up when a user enters 23 45 instead of 23.45. Notice that this routine cleans out the buffer with a nextLine at the end. int number; do { System.out.println("Enter a number between 1 and 10: "); if (sc.hasNextInt()) { // Check that there is an integer in the keyboard buffer number = sc.nextInt(); // // Check if the number is in range if (number < 1 || number > 10) { System.out.println("Number out of range."); } } else { // There was not an integer in the keyboard buffer number = -1; // a value that will keep execution in the loop System.out.println("You have not entered a number"); } sc.nextLine(); // Clean out the buffer } while (number < 1 || number > 10); This tells me that in the paving the onramp universe a readInt or readDbl must clear the keyboard buffer when it encounters a terminating character such as the space, tab, and \n. Already I can hear the roar over how this breaks the expected behaviour of Scanner such as allowing a list of primitives in the keyboard buffer. Ken From: Ethan McCue > Sent: November 7, 2024 11:43 AM To: Kenneth Fogel > Cc: discuss at openjdk.org Subject: Re: How about a readln for numbers? Currently, "don't mix nextLine with next/next int/etc" is an extremely common footgun. In one of the coding help discords, this is the auto message we send when people run into trouble with that. Mixing any nextXXX method with nextLine from the Scanner class for user input, will not ask you for input again but instead result in an empty line read by nextLine. To prevent this, when reading user input, always only use nextLine. If you need an int, do int value = Integer.parseInt(scanner.nextLine()); instead of using nextInt. Assume the following: Scanner scanner = new Scanner(System.in); System.out.println("Enter your age:"); int age = scanner.nextInt(); System.out.println("Enter your name:"); String name = scanner.nextLine(); System.out.println("Hello " + name + ", you are " + age + " years old"); When executing this code, you will be asked to enter an age, suppose you enter 20. However, the code will not ask you to actually input a name and the output will be: Hello , you are 20 years old. The reason why is that when you hit the enter button, your actual input is 20\n and not just 20. A call to nextInt will now consume the 20 and leave the newline symbol \n in the internal input buffer of System.in. The call to nextLine will now not lead to a new input, since there is still unread input left in System.in. So it will read the \n, leading to an empty input. So every user input is not only a number, but a full line. As such, it makes much more sense to also use nextLine(), even if reading just an age. The corrected code which works as intended is: Scanner scanner = new Scanner(System.in); System.out.println("Enter your age:"); // Now nextLine, not nextInt anymore int age = Integer.parseInt(scanner.nextLine()); System.out.println("Enter your name:"); String name = scanner.nextLine(); System.out.println("Hello " + name + ", you are " + age + " years old"); The nextXXX methods, such as nextInt can be useful when reading multi-input from a single line. For example when you enter 20 John in a single line. On Thu, Nov 7, 2024, 11:36 AM Kenneth Fogel > wrote: The readln method introduced as part java.base is great because it includes the prompt for the input. The only shortcoming is that if the input must be a number then you still need to employ a static class member such as Double.parseDouble() to make it a number: // Currently var loan = Double.parseDouble(readln("Loan: ")); // My delusional idea var loan = readDbl("Loan: "); I can think of reasons why this could be a bad idea. I taught my students to use a construct using Scanner to create a console input that passed my Beethoven test (while humming Beethoven?s 5th symphony randomly strike keys on your keyboard as if you were playing the symphony and your code should just report invalid input and not an exception). If we had a readInt and/or readDbl they would not pass this test. That Python would happily accept anything and then fail when the value was used in a calculation is no better. But, for learning Java could we have a readInt or readDbl alongside readln? I know that DataInputStream has such methods, but without a prompt and it must be attached to an input stream such as a file. As always, just thinking out loud. Feel free to use my name in vain. Ken -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Nov 7 19:40:42 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 7 Nov 2024 14:40:42 -0500 Subject: JEP 495 Question In-Reply-To: References: Message-ID: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Yes, these were considered, and dismissed with prejudice, in part because the model implied by readInt and readDouble is a problematic one, because it greatly complicates the "reading state" that has to be understood by the user (e.g., what if there's no digits on the line, does it read another line?? what if the digits end mid-line, does it leave the cursor there?? what if it consumes all the characters on the line, and then you read a string?)? These are issues that invariably trip up beginners and experienced users alike. The locution you suggest, which is to use readln() to read a line into a String, then parseInt() to convert to an int, is more wordy than some of the alternatives, but much clearer and simpler as to what is going on, because it composes two simple functions (read a string, convert a string to an int).? This is good for beginners, because they can see that there are steps here to get the data into the form they want.? The readInt() method provides the illusion of simplicity, but ends up moving significant complexity to where we are more likely to trip over it in the dark. I'm not saying there will never be a richer way to do this, but readInt() and friends are definitely not it. More generally, one of the worst things about creating an IO class is that it immediately becomes? target for a flood of "can you just add my one favorite method" requests.? We might add a few more methods to it in the next decade.? But we also might not; less is more. On 11/7/2024 12:06 PM, Barry Burd wrote: > I see that parameterless println() and readln() methods are being > added to java.io.IO in JEP 495. I'm still wondering if there's been > any thought to including methods like readInt and readDouble. In the > early stage of an introduction to Java, it's common to have the > student input int values and double values for simple examples. With > the current spec, the best way to do this (as far as I can see) is > > ????? var n = Integer.parseInt(readln("Enter a number")); > > It seems natural to simplify this with a readInt method, and the > addition of such a method wouldn't weigh heavily on the java.io.IO > package. > Any thoughts? > ? --Barry -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Nov 7 19:41:48 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 7 Nov 2024 14:41:48 -0500 Subject: How about a readln for numbers In-Reply-To: References: Message-ID: <33ad8b31-bef8-4ae2-98dd-810d75892e68@oracle.com> Short answer: Absolutely not. More discussion elsewhere in this thread. On 11/7/2024 2:16 PM, Kenneth Fogel wrote: > > The readln method introduced as part java.base is great because it > includes the prompt for the input. The only shortcoming is that if the > input must be a number then you still need to employ a static class > member such as Double.parseDouble() to make it a number: > > // Currently > > var loan = Double.parseDouble(readln("Loan: ")); > > // My delusional idea > > var loan = readDbl("Loan: "); > > I can think of reasons why this could be a bad idea. I taught my > students to use a construct using Scanner to create a console input > that passed my Beethoven test (while humming Beethoven?s 5^th symphony > randomly strike keys on your keyboard as if you were playing the > symphony and your code should just report invalid input and not an > exception). If we had a readInt and/or readDbl they would not pass > this test. That Python would happily accept anything and then fail > when the value was used in a calculation is no better. But, for > learning Java could we have a readInt or readDbl alongside readln? I > know that DataInputStream has such methods, but without a prompt and > it must be attached to an input stream such as a file. > > As always, just thinking out loud. Feel free to use my name in vain. > > Ken > > This message and a back and forth with Ethan McCue began in the > OpenJDK Discus list and we were asked to move it here. Here is the > summary of messages in the order they were sent to the list. The > message above is the last one. > > void main() { > ? ? int number; > ? ? do { > String input = readln("Enter a number between 1 and 10: "); > ? ? ? ? try { > number = Integer.parseInt(input); > if (number < 1 || number > 10) { > ? ? println("Number out of range"); > } > > // All is well > break; > ? ? ? ? } catch (NumberFormatException _) { > println("You have not entered a number"); > ? ? ? ? } > ? ? } while (true); > > println(number); > } > > For color on how that might affect the pedagogy: > > "First we need to get input from the user, this is a String which > represents text" > > void main() { > ? ? String input = readln("Enter a number between 1 and 10: "); > } > > "We can turn a String into an int by writing Integer.parseInt( here>). If what they wrote isn't a number it will crash" > > void main() { > ? ? String input = readln("Enter a number between 1 and 10: "); > ? ? int number = Integer.parseInt(input); > } > > "To handle crashes we can put the code that might crash inside of try > { }, then write the way it might crash in a catch clause" > > void main() { > ? ? String input = readln("Enter a number between 1 and 10: "); > ? ? try { > ? ? ? ? int number = Integer.parseInt(input); > ? ? ? ? println("You gave: " + number); > ? ? } catch (NumberFormatException e) { > ? ? ? ? println("Bad number: " + input); > ? ? } > } > > "If they gave a number that wasn't between 1 and 10 ..." > > void main() { > ? ? String input = readln("Enter a number between 1 and 10: "); > ? ? try { > ? ? ? ? int number = Integer.parseInt(input); > ? ? ? ? if (number < 1 || number > 10) { > println("Number is not between 1 and 10: " + number); > ? ? ? ? } > ? ? ? ? else { > println("You gave: " + number); > ? ? ? ? } > ? ? } catch (NumberFormatException e) { > println("Bad number: " + input); > ? ? } > } > > "Now we want to keep asking them until they give a valid input. To do > this, first delay the assignment of number" > > void main() { > ? ? String input = readln("Enter a number between 1 and 10: "); > ? ? int number; > ? ? try { > number = Integer.parseInt(input); > ? ? ? ? if (number < 1 || number > 10) { > println("Number is not between 1 and 10: " + number); > ? ? ? ? } > ? ? ? ? else { > println("You gave: " + number); > ? ? ? ? } > ? ? } catch (NumberFormatException e) { > println("Bad number: " + input); > ? ? } > } > > ... and so on until you reach > > > void main() { > ? ? String input = readln("Enter a number between 1 and 10: "); > > ? ? int number; > ? ? do { > ? ? ? ? try { > ? ? ? ? ? ? number = Integer.parseInt(input); > ? ? ? ? ? ? if (number < 1 || number > 10) { > ? ? ? ? ? ? ? ? println("Number is not between 1 and 10: " + number); > ? ? ? ? ? ? } > ? ? ? ? ? ? else { > ? ? ? ? ? ? ? ? break; > ? ? ? ? ? ? } > ? ? ? ? } catch (NumberFormatException e) { > ? ? ? ? ? ? println("Bad number: " + input); > ? ? ? ? } > ? ? } while (true); > > ? ? println("You gave: " + number); > } > > On Thu, Nov 7, 2024 at 12:50?PM Ethan McCue wrote: > > So with all that context, do you see why accepting > Double.parseDouble(readln());?might be worth it? > > "Paving the onramp" shouldn't mean only "paving the first 48 hours." > The convenience afforded by a dedicated readDouble, at least to me, > feels outweighed > > by > > * The loss of a perfectly good opportunity to explain the fundamentals > of parsing (strings represent text, you can interpret that text by....) > * The loss of a perfectly good opportunity to teach basic exception > handling > * The divergence with the behavior of Scanner > * The combinatorial explosion of "why not readByte?" > > * The privileged?position it puts primitives in as the end-point of > interpreting user input > * Guns, Feet > > (I've separately?voiced my concerns about readln?in this regard.) > > On Thu, Nov 7, 2024 at 12:40?PM Kenneth Fogel > wrote: > > Here is an example of a Beethoven test passing routine that expects a > number for 1 to 10. The Scanner, sc, has already been initialized. I > also taught how to use regular expressions to fine tune what is > acceptable input and the acceptable range such that you only need to > use readLine so that subsequent input is not messed up when a user > enters 23 45 instead of 23.45. Notice that this routine cleans out the > buffer with a nextLine at the end. > > ?int number; > > do { > > System.out.println("Enter a number between 1 and 10: "); > > if (sc.hasNextInt()) { // Check that there is an integer in the > keyboard buffer > > number = sc.nextInt(); // > > ????????????????// Check if the number is in range > > if (number < 1 || number > 10) { > > System.out.println("Number out of range."); > > } > > } else { // There was not an integer in the keyboard buffer > > number = -1; // a value that will keep execution in the loop > > System.out.println("You have not entered a number"); > > } > > sc.nextLine(); // Clean out the buffer > > } while (number < 1 || number > 10); > > This tells me that in the paving the onramp universe a readInt or > readDbl must clear the keyboard buffer when it encounters a > terminating character such as the space, tab, and \n. Already I can > hear the roar over how this breaks the expected behaviour of Scanner > such as allowing a list of primitives in the keyboard buffer. > > Ken > > *From:*Ethan McCue > *Sent:* November 7, 2024 11:43 AM > *To:* Kenneth Fogel > *Cc:* discuss at openjdk.org > *Subject:* Re: How about a readln for numbers? > > Currently, "don't mix nextLine with next/next int/etc" is an extremely > common footgun. > > In one of the coding help discords, this is the auto message we send > when people run into trouble with that. > > Mixing any |nextXXX|method with |nextLine|from the |Scanner|class for > user input, will not ask you for input again but instead result in an > empty line read by |nextLine|. To prevent this, when reading user > input, always only use |nextLine|. If you need an |int|, do > > int||value||=|Integer.parseInt(scanner.nextLine());| > > instead of using |nextInt|. Assume the following: > > Scanner||scanner||=||new||Scanner|(System.in);| > || > |System.out.println(|"Enter your age:"|);| > int||age||=|scanner.nextInt();| > |System.out.println(|"Enter your name:"|);| > String||name||=|scanner.nextLine();| > || > |System.out.println(|"Hello "|+ name + |", you are "|+ age + |" years old"|);| > > When executing this code, you will be asked to enter an age, suppose > you enter |20|. However, the code will not ask you to actually input a > name and the output will be: > > |Hello , you are |20|years old.| > > The reason why is that when you hit the |enter|button, your actual > input is > > 20|\n| > > and not just |20|. A call to |nextInt|will now consume the |20|and > leave the newline symbol |\n|in the internal input buffer of > |System.in|. The call to |nextLine|will now not lead to a new input, > since there is still unread input left in |System.in|. So it will read > the |\n|, leading to an empty input. So every user input is not only a > number, but a *full line*. As such, it makes much more sense to also > use |nextLine()|, even if reading just an age. The corrected code > which works as intended is: > > Scanner||scanner||=||new||Scanner|(System.in);| > || > |System.out.println(|"Enter your age:"|);| > // Now nextLine, not nextInt anymore > int||age||=|Integer.parseInt(scanner.nextLine());| > |System.out.println(|"Enter your name:"|);| > String||name||=|scanner.nextLine();| > || > |System.out.println(|"Hello "|+ name + |", you are "|+ age + |" years old"|);| > > The |nextXXX|methods, such as |nextInt|can be useful when reading > multi-input from a single line. For example when you enter |20 John|in > a single line. > > On Thu, Nov 7, 2024, 11:36 AM Kenneth Fogel > wrote: > > The readln method introduced as part java.base is great because it > includes the prompt for the input. The only shortcoming is that if the > input must be a number then you still need to employ a static class > member such as Double.parseDouble() to make it a number: > > // Currently > > var loan = Double.parseDouble(readln("Loan: ")); > > // My delusional idea > > var loan = readDbl("Loan: "); > > I can think of reasons why this could be a bad idea. I taught my > students to use a construct using Scanner to create a console input > that passed my Beethoven test (while humming Beethoven?s 5^th symphony > randomly strike keys on your keyboard as if you were playing the > symphony and your code should just report invalid input and not an > exception). If we had a readInt and/or readDbl they would not pass > this test. That Python would happily accept anything and then fail > when the value was used in a calculation is no better. But, for > learning Java could we have a readInt or readDbl alongside readln? I > know that DataInputStream has such methods, but without a prompt and > it must be attached to an input stream such as a file. > > As always, just thinking out loud. Feel free to use my name in vain. > > Ken > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kfogel at dawsoncollege.qc.ca Thu Nov 7 20:46:40 2024 From: kfogel at dawsoncollege.qc.ca (Kenneth Fogel) Date: Thu, 7 Nov 2024 20:46:40 +0000 Subject: JEP 495 Question In-Reply-To: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: I understand your position and ten years ago I would agree with it wholeheartedly but not anymore. I am excited about JEP 445 and how it might bring more students and self learners to Java. Today?s learners want fast results. It is why I believe so many are hooked on Python. I dislike Python because it is an untyped language, but we cannot ignore that: Python: loan = input("Loan: ") makes more sense to those starting out than: Java: var loan = Double.parseDouble(readln("Loan: ")); Both will throw exceptions if you enter ?bob? for the value, just at different points in the execution. I feel that any language should evolve not only with new features but in new ways to learn that match how people learn today. I do agree that there can easily be a flood of requests that could probably make Java look like GW-BASIC. Thank you, Brian, for providing the summary of why you and the Java team decided not to have a numeric read. As they say, we will have to agree to disagree. Ken From: amber-dev On Behalf Of Brian Goetz Sent: November 7, 2024 2:41 PM To: Barry Burd ; amber-dev at openjdk.org Subject: Re: JEP 495 Question Yes, these were considered, and dismissed with prejudice, in part because the model implied by readInt and readDouble is a problematic one, because it greatly complicates the "reading state" that has to be understood by the user (e.g., what if there's no digits on the line, does it read another line? what if the digits end mid-line, does it leave the cursor there? what if it consumes all the characters on the line, and then you read a string?) These are issues that invariably trip up beginners and experienced users alike. The locution you suggest, which is to use readln() to read a line into a String, then parseInt() to convert to an int, is more wordy than some of the alternatives, but much clearer and simpler as to what is going on, because it composes two simple functions (read a string, convert a string to an int). This is good for beginners, because they can see that there are steps here to get the data into the form they want. The readInt() method provides the illusion of simplicity, but ends up moving significant complexity to where we are more likely to trip over it in the dark. I'm not saying there will never be a richer way to do this, but readInt() and friends are definitely not it. More generally, one of the worst things about creating an IO class is that it immediately becomes target for a flood of "can you just add my one favorite method" requests. We might add a few more methods to it in the next decade. But we also might not; less is more. On 11/7/2024 12:06 PM, Barry Burd wrote: I see that parameterless println() and readln() methods are being added to java.io.IO in JEP 495. I'm still wondering if there's been any thought to including methods like readInt and readDouble. In the early stage of an introduction to Java, it's common to have the student input int values and double values for simple examples. With the current spec, the best way to do this (as far as I can see) is var n = Integer.parseInt(readln("Enter a number")); It seems natural to simplify this with a readInt method, and the addition of such a method wouldn't weigh heavily on the java.io.IO package. Any thoughts? --Barry -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Thu Nov 7 21:00:21 2024 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 7 Nov 2024 22:00:21 +0100 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: Hello! I agree that methods like readInt and readDouble should be added, if we really want to make Java appealing for beginners who try to write console applications. It looks to me that sometimes we think too much and avoid making decisions and taking responsibility if there's no single obviously good way to solve the problem. Here any decent solution would be good enough, were it just a shortcut to parseDouble(readln()) or having a Scanner.nextDouble under the hood. Either way is much better than having no solution at all. Students will get used to it, and we can easily ignore nitpickers who would criticize it and insist that the opposite solution should be implemented. Because nitpickers will do this in any case. I personally would prefer even more beginner-friendly solution, like repeatedly asking for a number and displaying a friendly error message if the user types something different (without throwing an exception). Sure, this is not flexible and should not be used in production-quality applications. But this is what would be the least scary for beginners. I also see the desire to keep the API surface not bloated, but you can see how many people connected to education actually requested methods to read numbers in this mailing list. Do you remember them requesting anything else? To me it's clear that reading numbers in a one method call is a single most wanted addition to IO. You'll see many more such requests after the feature is graduated (well, unless every single student moves to Python). With best regards, Tagir Valeev On Thu, Nov 7, 2024 at 9:47?PM Kenneth Fogel wrote: > I understand your position and ten years ago I would agree with it > wholeheartedly but not anymore. I am excited about JEP 445 and how it might > bring more students and self learners to Java. Today?s learners want fast > results. It is why I believe so many are hooked on Python. I dislike Python > because it is an untyped language, but we cannot ignore that: > > > > Python: loan = input("Loan: ") > > makes more sense to those starting out than: > > Java: var loan = Double.parseDouble(readln("Loan: ")); > > > > Both will throw exceptions if you enter ?bob? for the value, just at > different points in the execution. > > > > I feel that any language should evolve not only with new features but in > new ways to learn that match how people learn today. I do agree that there > can easily be a flood of requests that could probably make Java look like > GW-BASIC. > > > > Thank you, Brian, for providing the summary of why you and the Java team > decided not to have a numeric read. As they say, we will have to agree to > disagree. > > > > Ken > > > > > > > > > > > > *From:* amber-dev *On Behalf Of *Brian Goetz > *Sent:* November 7, 2024 2:41 PM > *To:* Barry Burd ; amber-dev at openjdk.org > *Subject:* Re: JEP 495 Question > > > > Yes, these were considered, and dismissed with prejudice, in part because > the model implied by readInt and readDouble is a problematic one, because > it greatly complicates the "reading state" that has to be understood by the > user (e.g., what if there's no digits on the line, does it read another > line? what if the digits end mid-line, does it leave the cursor there? > what if it consumes all the characters on the line, and then you read a > string?) These are issues that invariably trip up beginners and > experienced users alike. > > The locution you suggest, which is to use readln() to read a line into a > String, then parseInt() to convert to an int, is more wordy than some of > the alternatives, but much clearer and simpler as to what is going on, > because it composes two simple functions (read a string, convert a string > to an int). This is good for beginners, because they can see that there > are steps here to get the data into the form they want. The readInt() > method provides the illusion of simplicity, but ends up moving significant > complexity to where we are more likely to trip over it in the dark. > > I'm not saying there will never be a richer way to do this, but readInt() > and friends are definitely not it. > > More generally, one of the worst things about creating an IO class is that > it immediately becomes target for a flood of "can you just add my one > favorite method" requests. We might add a few more methods to it in the > next decade. But we also might not; less is more. > > On 11/7/2024 12:06 PM, Barry Burd wrote: > > I see that parameterless println() and readln() methods are being added to > java.io.IO in JEP 495. I'm still wondering if there's been any thought to > including methods like readInt and readDouble. In the early stage of an > introduction to Java, it's common to have the student input int values and > double values for simple examples. With the current spec, the best way to > do this (as far as I can see) is > > var n = Integer.parseInt(readln("Enter a number")); > > It seems natural to simplify this with a readInt method, and the addition > of such a method wouldn't weigh heavily on the java.io.IO package. > Any thoughts? > --Barry > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Nov 7 21:05:02 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 7 Nov 2024 15:05:02 -0600 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: On Thu, Nov 7, 2024 at 2:47?PM Kenneth Fogel wrote: > I dislike Python because it is an untyped language, but we cannot ignore > that: > > > > Python: loan = input("Loan: ") > > makes more sense to those starting out than: > > Java: var loan = Double.parseDouble(readln("Loan: ")); > I perceive a disconnect here between the objective and the assumed mechanism to achieve that objective. If the objective is "Provide a friendly environment for people starting out learning Java to get familiar with it", why does the mechanism have to be "Make changes to the language" ? For example, couldn't you use JShell to build a sufficiently "friendly environment"? $ cat friendly.jsh double input(String prompt) throws Exception { System.console().printf("%s", prompt); return Double.parseDouble(System.console().readLine()); } $ jshell friendly.jsh | Welcome to JShell -- Version 23 | For an introduction type: /help intro jshell> var x = input("Loan: ") Loan: 123 x ==> 123.0 Etc. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Thu Nov 7 21:08:42 2024 From: ron.pressler at oracle.com (Ron Pressler) Date: Thu, 7 Nov 2024 21:08:42 +0000 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: Even conceding the possibility that this is true, the question remains *to what extent* it's true. I think you?ll agree that we?ve made progress toward where you think we should be that is bigger than the remaining delta. If at some point we learn that the lack of a readlnInt method is a major hurdle to learning Java and that that method is what stands between us and world domination, perhaps we shouldn?t stand on principle and should reconsider. But given that we?ve only recently made an even bigger step, one whose impact we?ve yet to study, I don?t see the urgency in reconsidering the more principled stance at this time. On the matter of beginner-friendliness, I think we have bigger fish to fry than that method. ? Ron > On 7 Nov 2024, at 20:46, Kenneth Fogel wrote: > > I understand your position and ten years ago I would agree with it wholeheartedly but not anymore. I am excited about JEP 445 and how it might bring more students and self learners to Java. Today?s learners want fast results. It is why I believe so many are hooked on Python. I dislike Python because it is an untyped language, but we cannot ignore that: > Python: loan = input("Loan: ") > makes more sense to those starting out than: > Java: var loan = Double.parseDouble(readln("Loan: ")); > Both will throw exceptions if you enter ?bob? for the value, just at different points in the execution. > I feel that any language should evolve not only with new features but in new ways to learn that match how people learn today. I do agree that there can easily be a flood of requests that could probably make Java look like GW-BASIC. > Thank you, Brian, for providing the summary of why you and the Java team decided not to have a numeric read. As they say, we will have to agree to disagree. > Ken From brian.goetz at oracle.com Thu Nov 7 21:09:25 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 7 Nov 2024 16:09:25 -0500 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: Here's the bar we're envisioning: methods that are automatically imported in this way _effectively become part of the langauge_.? The bar for that is very high. println(String) clears that bar; a language without console output is pretty suspect. readSomeCharsAndParseThemAsAnIntegerAndLeaveTheInputInSomeUnspecifiedState() does not. This doesn't mean there can't be something _elsewhere in the JDK_ for this purpose, or even many somethings.? But if you couldn't credibly say "this should be part of every language", then it almost surely does not meet the bar for IO. But these methods are so flawed that they obviously fail this bar.? (And, they also serve as great "my first method" examples, for students learning simple abstraction!) On 11/7/2024 4:00 PM, Tagir Valeev wrote: > Hello! > > I agree that methods like readInt and readDouble should be added, if > we really want to make Java appealing for beginners who try to write > console applications. It looks to me that sometimes we think too much > and avoid making decisions and taking responsibility if there's no > single obviously good way to solve the problem. Here any decent > solution would be good enough, were it just a shortcut to > parseDouble(readln()) or having a Scanner.nextDouble under the hood. > Either?way is much better than having no solution at all. Students > will get used to it, and we can easily ignore nitpickers who would > criticize it and insist that the opposite solution should be > implemented. Because nitpickers will do this in any case. > > I personally would prefer even more beginner-friendly solution, like > repeatedly asking for a number and displaying a friendly error message > if the user types something different (without throwing an exception). > Sure, this is not flexible and should not be used in > production-quality applications. But this is what would be the least > scary for beginners. > > I also see the?desire to keep the API surface not bloated, but you can > see how many people?connected to education actually requested methods > to read numbers in this mailing list. Do you remember them requesting > anything else? To me it's clear that reading numbers in a one method > call is a single most wanted addition to IO. You'll see many more such > requests after the feature is graduated (well, unless every single > student moves to Python). > > With best regards, > Tagir Valeev > > On Thu, Nov 7, 2024 at 9:47?PM Kenneth Fogel > wrote: > > I understand your position and ten years ago I would agree with it > wholeheartedly but not anymore. I am excited about JEP 445 and how > it might bring more students and self learners to Java. Today?s > learners want fast results. It is why I believe so many are hooked > on Python. I dislike Python because it is an untyped language, but > we cannot ignore that: > > Python: loan = input("Loan: ") > > makes more sense to those starting out than: > > Java: var loan = Double.parseDouble(readln("Loan: ")); > > Both will throw exceptions if you enter ?bob? for the value, just > at different points in the execution. > > I feel that any language should evolve not only with new features > but in new ways to learn that match how people learn today. I do > agree that there can easily be a flood of requests that could > probably make Java look like GW-BASIC. > > Thank you, Brian, for providing the summary of why you and the > Java team decided not to have a numeric read. As they say, we will > have to agree to disagree. > > Ken > > *From:*amber-dev *On Behalf Of *Brian > Goetz > *Sent:* November 7, 2024 2:41 PM > *To:* Barry Burd ; amber-dev at openjdk.org > *Subject:* Re: JEP 495 Question > > Yes, these were considered, and dismissed with prejudice, in part > because the model implied by readInt and readDouble is a > problematic one, because it greatly complicates the "reading > state" that has to be understood by the user (e.g., what if > there's no digits on the line, does it read another line?? what if > the digits end mid-line, does it leave the cursor there?? what if > it consumes all the characters on the line, and then you read a > string?)? These are issues that invariably trip up beginners and > experienced users alike. > > The locution you suggest, which is to use readln() to read a line > into a String, then parseInt() to convert to an int, is more wordy > than some of the alternatives, but much clearer and simpler as to > what is going on, because it composes two simple functions (read a > string, convert a string to an int).? This is good for beginners, > because they can see that there are steps here to get the data > into the form they want.? The readInt() method provides the > illusion of simplicity, but ends up moving significant complexity > to where we are more likely to trip over it in the dark. > > I'm not saying there will never be a richer way to do this, but > readInt() and friends are definitely not it. > > More generally, one of the worst things about creating an IO class > is that it immediately becomes target for a flood of "can you just > add my one favorite method" requests.? We might add a few more > methods to it in the next decade.? But we also might not; less is > more. > > On 11/7/2024 12:06 PM, Barry Burd wrote: > > I see that parameterless println() and readln() methods are > being added to java.io.IO > > in JEP 495. I'm still wondering if there's been any thought to > including methods like readInt and readDouble. In the early > stage of an introduction to Java, it's common to have the > student input int values and double values for simple > examples. With the current spec, the best way to do this (as > far as I can see) is > > ????? var n = Integer.parseInt(readln("Enter a number")); > > It seems natural to simplify this with a readInt method, and > the addition of such a method wouldn't weigh heavily on the > java.io.IO > > package. > Any thoughts? > ? --Barry > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Thu Nov 7 21:13:32 2024 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 7 Nov 2024 22:13:32 +0100 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: Well, it took years to convince you to add Stream.toList(), and the reasoning against this method was also very strong. We can wait ;?-?) Tagir On Thu, Nov 7, 2024, 22:09 Brian Goetz wrote: > Here's the bar we're envisioning: methods that are automatically imported > in this way _effectively become part of the langauge_. The bar for that is > very high. > > println(String) clears that bar; a language without console output is > pretty suspect. > readSomeCharsAndParseThemAsAnIntegerAndLeaveTheInputInSomeUnspecifiedState() > does not. > > This doesn't mean there can't be something _elsewhere in the JDK_ for this > purpose, or even many somethings. But if you couldn't credibly say "this > should be part of every language", then it almost surely does not meet the > bar for IO. > > But these methods are so flawed that they obviously fail this bar. (And, > they also serve as great "my first method" examples, for students learning > simple abstraction!) > > > > > > On 11/7/2024 4:00 PM, Tagir Valeev wrote: > > Hello! > > I agree that methods like readInt and readDouble should be added, if we > really want to make Java appealing for beginners who try to write console > applications. It looks to me that sometimes we think too much and avoid > making decisions and taking responsibility if there's no single obviously > good way to solve the problem. Here any decent solution would be good > enough, were it just a shortcut to parseDouble(readln()) or having a > Scanner.nextDouble under the hood. Either way is much better than having no > solution at all. Students will get used to it, and we can easily ignore > nitpickers who would criticize it and insist that the opposite solution > should be implemented. Because nitpickers will do this in any case. > > I personally would prefer even more beginner-friendly solution, like > repeatedly asking for a number and displaying a friendly error message if > the user types something different (without throwing an exception). Sure, > this is not flexible and should not be used in production-quality > applications. But this is what would be the least scary for beginners. > > I also see the desire to keep the API surface not bloated, but you can see > how many people connected to education actually requested methods to read > numbers in this mailing list. Do you remember them requesting anything > else? To me it's clear that reading numbers in a one method call is a > single most wanted addition to IO. You'll see many more such requests after > the feature is graduated (well, unless every single student moves to > Python). > > With best regards, > Tagir Valeev > > On Thu, Nov 7, 2024 at 9:47?PM Kenneth Fogel > wrote: > >> I understand your position and ten years ago I would agree with it >> wholeheartedly but not anymore. I am excited about JEP 445 and how it might >> bring more students and self learners to Java. Today?s learners want fast >> results. It is why I believe so many are hooked on Python. I dislike Python >> because it is an untyped language, but we cannot ignore that: >> >> >> >> Python: loan = input("Loan: ") >> >> makes more sense to those starting out than: >> >> Java: var loan = Double.parseDouble(readln("Loan: ")); >> >> >> >> Both will throw exceptions if you enter ?bob? for the value, just at >> different points in the execution. >> >> >> >> I feel that any language should evolve not only with new features but in >> new ways to learn that match how people learn today. I do agree that there >> can easily be a flood of requests that could probably make Java look like >> GW-BASIC. >> >> >> >> Thank you, Brian, for providing the summary of why you and the Java team >> decided not to have a numeric read. As they say, we will have to agree to >> disagree. >> >> >> >> Ken >> >> >> >> >> >> >> >> >> >> >> >> *From:* amber-dev *On Behalf Of *Brian Goetz >> *Sent:* November 7, 2024 2:41 PM >> *To:* Barry Burd ; amber-dev at openjdk.org >> *Subject:* Re: JEP 495 Question >> >> >> >> Yes, these were considered, and dismissed with prejudice, in part because >> the model implied by readInt and readDouble is a problematic one, because >> it greatly complicates the "reading state" that has to be understood by the >> user (e.g., what if there's no digits on the line, does it read another >> line? what if the digits end mid-line, does it leave the cursor there? >> what if it consumes all the characters on the line, and then you read a >> string?) These are issues that invariably trip up beginners and >> experienced users alike. >> >> The locution you suggest, which is to use readln() to read a line into a >> String, then parseInt() to convert to an int, is more wordy than some of >> the alternatives, but much clearer and simpler as to what is going on, >> because it composes two simple functions (read a string, convert a string >> to an int). This is good for beginners, because they can see that there >> are steps here to get the data into the form they want. The readInt() >> method provides the illusion of simplicity, but ends up moving significant >> complexity to where we are more likely to trip over it in the dark. >> >> I'm not saying there will never be a richer way to do this, but readInt() >> and friends are definitely not it. >> >> More generally, one of the worst things about creating an IO class is >> that it immediately becomes target for a flood of "can you just add my one >> favorite method" requests. We might add a few more methods to it in the >> next decade. But we also might not; less is more. >> >> On 11/7/2024 12:06 PM, Barry Burd wrote: >> >> I see that parameterless println() and readln() methods are being added >> to java.io.IO >> >> in JEP 495. I'm still wondering if there's been any thought to including >> methods like readInt and readDouble. In the early stage of an introduction >> to Java, it's common to have the student input int values and double values >> for simple examples. With the current spec, the best way to do this (as far >> as I can see) is >> >> var n = Integer.parseInt(readln("Enter a number")); >> >> It seems natural to simplify this with a readInt method, and the addition >> of such a method wouldn't weigh heavily on the java.io.IO >> >> package. >> Any thoughts? >> --Barry >> >> >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Thu Nov 7 22:22:29 2024 From: ethan at mccue.dev (Ethan McCue) Date: Thu, 7 Nov 2024 17:22:29 -0500 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: > Today?s learners want fast results. I will opine past my expertise and say that's more a symptom of other education system problems. But more importantly, the learners of today being different from the learners of yesterday feels like evidence that the learners of tomorrow might be even more different still. Curriculums could change to account for that maybe, but a language would be stuck. On Thu, Nov 7, 2024, 5:12 PM Kenneth Fogel wrote: > I understand your position and ten years ago I would agree with it > wholeheartedly but not anymore. I am excited about JEP 445 and how it might > bring more students and self learners to Java. Today?s learners want fast > results. It is why I believe so many are hooked on Python. I dislike Python > because it is an untyped language, but we cannot ignore that: > > > > Python: loan = input("Loan: ") > > makes more sense to those starting out than: > > Java: var loan = Double.parseDouble(readln("Loan: ")); > > > > Both will throw exceptions if you enter ?bob? for the value, just at > different points in the execution. > > > > I feel that any language should evolve not only with new features but in > new ways to learn that match how people learn today. I do agree that there > can easily be a flood of requests that could probably make Java look like > GW-BASIC. > > > > Thank you, Brian, for providing the summary of why you and the Java team > decided not to have a numeric read. As they say, we will have to agree to > disagree. > > > > Ken > > > > > > > > > > > > *From:* amber-dev *On Behalf Of *Brian Goetz > *Sent:* November 7, 2024 2:41 PM > *To:* Barry Burd ; amber-dev at openjdk.org > *Subject:* Re: JEP 495 Question > > > > Yes, these were considered, and dismissed with prejudice, in part because > the model implied by readInt and readDouble is a problematic one, because > it greatly complicates the "reading state" that has to be understood by the > user (e.g., what if there's no digits on the line, does it read another > line? what if the digits end mid-line, does it leave the cursor there? > what if it consumes all the characters on the line, and then you read a > string?) These are issues that invariably trip up beginners and > experienced users alike. > > The locution you suggest, which is to use readln() to read a line into a > String, then parseInt() to convert to an int, is more wordy than some of > the alternatives, but much clearer and simpler as to what is going on, > because it composes two simple functions (read a string, convert a string > to an int). This is good for beginners, because they can see that there > are steps here to get the data into the form they want. The readInt() > method provides the illusion of simplicity, but ends up moving significant > complexity to where we are more likely to trip over it in the dark. > > I'm not saying there will never be a richer way to do this, but readInt() > and friends are definitely not it. > > More generally, one of the worst things about creating an IO class is that > it immediately becomes target for a flood of "can you just add my one > favorite method" requests. We might add a few more methods to it in the > next decade. But we also might not; less is more. > > On 11/7/2024 12:06 PM, Barry Burd wrote: > > I see that parameterless println() and readln() methods are being added to > java.io.IO in JEP 495. I'm still wondering if there's been any thought to > including methods like readInt and readDouble. In the early stage of an > introduction to Java, it's common to have the student input int values and > double values for simple examples. With the current spec, the best way to > do this (as far as I can see) is > > var n = Integer.parseInt(readln("Enter a number")); > > It seems natural to simplify this with a readInt method, and the addition > of such a method wouldn't weigh heavily on the java.io.IO package. > Any thoughts? > --Barry > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Fri Nov 8 07:44:20 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Fri, 8 Nov 2024 09:44:20 +0200 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: Just a remark: python IS typed, and code you have provided will result in loan being string, unsuitable for numeric operations. Correct input would be: loan = float(input(...)) Which starts to look pretty much the same except for absence of classs qualifier near float() Dont confuse good languages with js :) On Thu, Nov 7, 2024, 23:46 Kenneth Fogel wrote: > I understand your position and ten years ago I would agree with it > wholeheartedly but not anymore. I am excited about JEP 445 and how it might > bring more students and self learners to Java. Today?s learners want fast > results. It is why I believe so many are hooked on Python. I dislike Python > because it is an untyped language, but we cannot ignore that: > > > > Python: loan = input("Loan: ") > > makes more sense to those starting out than: > > Java: var loan = Double.parseDouble(readln("Loan: ")); > > > > Both will throw exceptions if you enter ?bob? for the value, just at > different points in the execution. > > > > I feel that any language should evolve not only with new features but in > new ways to learn that match how people learn today. I do agree that there > can easily be a flood of requests that could probably make Java look like > GW-BASIC. > > > > Thank you, Brian, for providing the summary of why you and the Java team > decided not to have a numeric read. As they say, we will have to agree to > disagree. > > > > Ken > > > > > > > > > > > > *From:* amber-dev *On Behalf Of *Brian Goetz > *Sent:* November 7, 2024 2:41 PM > *To:* Barry Burd ; amber-dev at openjdk.org > *Subject:* Re: JEP 495 Question > > > > Yes, these were considered, and dismissed with prejudice, in part because > the model implied by readInt and readDouble is a problematic one, because > it greatly complicates the "reading state" that has to be understood by the > user (e.g., what if there's no digits on the line, does it read another > line? what if the digits end mid-line, does it leave the cursor there? > what if it consumes all the characters on the line, and then you read a > string?) These are issues that invariably trip up beginners and > experienced users alike. > > The locution you suggest, which is to use readln() to read a line into a > String, then parseInt() to convert to an int, is more wordy than some of > the alternatives, but much clearer and simpler as to what is going on, > because it composes two simple functions (read a string, convert a string > to an int). This is good for beginners, because they can see that there > are steps here to get the data into the form they want. The readInt() > method provides the illusion of simplicity, but ends up moving significant > complexity to where we are more likely to trip over it in the dark. > > I'm not saying there will never be a richer way to do this, but readInt() > and friends are definitely not it. > > More generally, one of the worst things about creating an IO class is that > it immediately becomes target for a flood of "can you just add my one > favorite method" requests. We might add a few more methods to it in the > next decade. But we also might not; less is more. > > On 11/7/2024 12:06 PM, Barry Burd wrote: > > I see that parameterless println() and readln() methods are being added to > java.io.IO in JEP 495. I'm still wondering if there's been any thought to > including methods like readInt and readDouble. In the early stage of an > introduction to Java, it's common to have the student input int values and > double values for simple examples. With the current spec, the best way to > do this (as far as I can see) is > > var n = Integer.parseInt(readln("Enter a number")); > > It seems natural to simplify this with a readInt method, and the addition > of such a method wouldn't weigh heavily on the java.io.IO package. > Any thoughts? > --Barry > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From scolebourne at joda.org Fri Nov 8 09:20:59 2024 From: scolebourne at joda.org (Stephen Colebourne) Date: Fri, 8 Nov 2024 09:20:59 +0000 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: On Thu, 7 Nov 2024 at 21:10, Brian Goetz wrote: > Here's the bar we're envisioning: methods that are automatically imported in this way _effectively become part of the langauge_. The bar for that is very high. Making a method like readInt() "effectively part of the language" is clearly wrong. And therefore obvious why it should be rejected. But maybe the answer is to take a step back and reconsider whether the "automatically imported" part of the sentence is a bad choice (which therefore nullifies the "effectively become part of the language" claim, effectively allowing readInt and friends). The JEP ("Growing a program" section) asks users that migrate from a simple source file to a normal one to add two imports: import static java.io.IO.* import module java.base I'm OK with `import module java.base`, but I'm afraid I think `import static java.io.IO.*` is a really terrible idea. Most production code should not be using the console at all, it should be using some kind of logging system. System.out is a hack. IMO, the "growing the language" approach described in the JEP will end up over time normalising console output in the minds of developers, with a negative effect on production Java code. Given this, and working backwards (IMO): - Encouraging `import static java.io.IO.*` is a Really Bad Idea - Therefore migrating from simple source file should therefore only require `import module java.base` - Therefore, simple source files should use IO.println(String), not just println(String) - Therefore, more methods can be freely added to IO, like readInt() Yes, yes, yes. I know. `IO.println(String)` is more complex than just `println(String)` for a beginner. But is it really that bad for a beginner given the risk of normalising console usage? Stephen From rotanolexandr842 at gmail.com Fri Nov 8 09:32:43 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Fri, 8 Nov 2024 11:32:43 +0200 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: I feel like I have to disagree here. "Growing" is not about jumping from console calculator to spring boot aot compiled microservices with distributed tracing and other buzzwords. It is about jumping from one-file console calculator to n-files console calculator, assume on the stage of introducing packages and imports. IO will be used in production as often as System.console() (never, I would say), because modern logging and debug tools are just more convenient. All of this is my opinion, I am not stating it as a fact, hovewer I am strongly convinced in all of the above. And once again, production code is completely different world, and anything that is locked inside classes a that are not even allowed to have a package is not going to have any affect on people that are getting familiar with modern web, just because of how far this points are in learning path On Fri, Nov 8, 2024, 11:21 Stephen Colebourne wrote: > On Thu, 7 Nov 2024 at 21:10, Brian Goetz wrote: > > Here's the bar we're envisioning: methods that are automatically > imported in this way _effectively become part of the langauge_. The bar > for that is very high. > > Making a method like readInt() "effectively part of the language" is > clearly wrong. And therefore obvious why it should be rejected. > But maybe the answer is to take a step back and reconsider whether the > "automatically imported" part of the sentence is a bad choice (which > therefore nullifies the "effectively become part of the language" > claim, effectively allowing readInt and friends). > > The JEP ("Growing a program" section) asks users that migrate from a > simple source file to a normal one to add two imports: > import static java.io.IO.* > import module java.base > > I'm OK with `import module java.base`, but I'm afraid I think `import > static java.io.IO.*` is a really terrible idea. Most production code > should not be using the console at all, it should be using some kind > of logging system. System.out is a hack. IMO, the "growing the > language" approach described in the JEP will end up over time > normalising console output in the minds of developers, with a negative > effect on production Java code. > > Given this, and working backwards (IMO): > - Encouraging `import static java.io.IO.*` is a Really Bad Idea > - Therefore migrating from simple source file should therefore only > require `import module java.base` > - Therefore, simple source files should use IO.println(String), not > just println(String) > - Therefore, more methods can be freely added to IO, like readInt() > > Yes, yes, yes. I know. `IO.println(String)` is more complex than just > `println(String)` for a beginner. But is it really that bad for a > beginner given the risk of normalising console usage? > > Stephen > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kfogel at dawsoncollege.qc.ca Fri Nov 8 14:24:42 2024 From: kfogel at dawsoncollege.qc.ca (Kenneth Fogel) Date: Fri, 8 Nov 2024 14:24:42 +0000 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: I tested the code that line was from using PyCharm as my IDE and it ran without any errors or warnings. When I entered a string such as ?Bob? rather than ?5000? an error occurred at run time in the line of code that calculated with the loan variable. I am not a Python expert but I suspect that you are using a feature that allows Python to recognize that the string is not a number at the point of input. This is not an example of a typed language. I will test this further this morning. Ken ________________________________ From: Olexandr Rotan Sent: Friday, November 8, 2024 2:44:20 AM To: Kenneth Fogel ; amber-dev Subject: Re: JEP 495 Question Just a remark: python IS typed, and code you have provided will result in loan being string, unsuitable for numeric operations. Correct input would be: loan = float(input(...)) Which starts to look pretty much the same except for absence of classs qualifier near float() Dont confuse good languages with js :) On Thu, Nov 7, 2024, 23:46 Kenneth Fogel > wrote: I understand your position and ten years ago I would agree with it wholeheartedly but not anymore. I am excited about JEP 445 and how it might bring more students and self learners to Java. Today?s learners want fast results. It is why I believe so many are hooked on Python. I dislike Python because it is an untyped language, but we cannot ignore that: Python: loan = input("Loan: ") makes more sense to those starting out than: Java: var loan = Double.parseDouble(readln("Loan: ")); Both will throw exceptions if you enter ?bob? for the value, just at different points in the execution. I feel that any language should evolve not only with new features but in new ways to learn that match how people learn today. I do agree that there can easily be a flood of requests that could probably make Java look like GW-BASIC. Thank you, Brian, for providing the summary of why you and the Java team decided not to have a numeric read. As they say, we will have to agree to disagree. Ken From: amber-dev > On Behalf Of Brian Goetz Sent: November 7, 2024 2:41 PM To: Barry Burd >; amber-dev at openjdk.org Subject: Re: JEP 495 Question Yes, these were considered, and dismissed with prejudice, in part because the model implied by readInt and readDouble is a problematic one, because it greatly complicates the "reading state" that has to be understood by the user (e.g., what if there's no digits on the line, does it read another line? what if the digits end mid-line, does it leave the cursor there? what if it consumes all the characters on the line, and then you read a string?) These are issues that invariably trip up beginners and experienced users alike. The locution you suggest, which is to use readln() to read a line into a String, then parseInt() to convert to an int, is more wordy than some of the alternatives, but much clearer and simpler as to what is going on, because it composes two simple functions (read a string, convert a string to an int). This is good for beginners, because they can see that there are steps here to get the data into the form they want. The readInt() method provides the illusion of simplicity, but ends up moving significant complexity to where we are more likely to trip over it in the dark. I'm not saying there will never be a richer way to do this, but readInt() and friends are definitely not it. More generally, one of the worst things about creating an IO class is that it immediately becomes target for a flood of "can you just add my one favorite method" requests. We might add a few more methods to it in the next decade. But we also might not; less is more. On 11/7/2024 12:06 PM, Barry Burd wrote: I see that parameterless println() and readln() methods are being added to java.io.IO in JEP 495. I'm still wondering if there's been any thought to including methods like readInt and readDouble. In the early stage of an introduction to Java, it's common to have the student input int values and double values for simple examples. With the current spec, the best way to do this (as far as I can see) is var n = Integer.parseInt(readln("Enter a number")); It seems natural to simplify this with a readInt method, and the addition of such a method wouldn't weigh heavily on the java.io.IO package. Any thoughts? --Barry -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Fri Nov 8 15:24:32 2024 From: ethan at mccue.dev (Ethan McCue) Date: Fri, 8 Nov 2024 10:24:32 -0500 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: While there is a deeper dive to do on why exactly JShell does not fit the bill, I don't think this is actually an objective: > Provide a friendly environment for people starting out learning Java to get familiar with it At least not as a terminal goal. Yes, that's how we have been talking about it. It's not really accurate though. The *goal* is seems more akin to "improve the experience of learning and teaching java as a first language". Learning and teaching don't stop after the first week or two. Classes not being front-loaded has the potential to deeply affect the order in which things are taught. The JEP kinda hints at this with * Offer a smooth on-ramp to Java programming, so that instructors can introduce concepts in a gradual manner. * Help students write basic programs in a concise manner, and grow their code gracefully as their skills grow. I think the metaphor of an on-ramp might be limiting imagination. When you get on the on ramp you are there for a short period of time and then get straight into the flow of traffic. You "get up to speed" and then go full speed from then on. If I'm clawing for a metaphor I'd say I care more about having a "gentle slope." Yes, getting to the top of the mountain might take 5 hours. But so long as we reduce or minimize the times you need to break out climbing gear or have points in the map that say "exercise for the reader" that's fine. On Thu, Nov 7, 2024 at 6:31?PM Archie Cobbs wrote: > On Thu, Nov 7, 2024 at 2:47?PM Kenneth Fogel > wrote: > >> I dislike Python because it is an untyped language, but we cannot ignore >> that: >> >> >> >> Python: loan = input("Loan: ") >> >> makes more sense to those starting out than: >> >> Java: var loan = Double.parseDouble(readln("Loan: ")); >> > > I perceive a disconnect here between the objective and the assumed > mechanism to achieve that objective. > > If the objective is "Provide a friendly environment for people starting > out learning Java to get familiar with it", why does the mechanism have to > be "Make changes to the language" ? > > For example, couldn't you use JShell to build a sufficiently "friendly > environment"? > > $ cat friendly.jsh > double input(String prompt) throws Exception { > System.console().printf("%s", prompt); > return Double.parseDouble(System.console().readLine()); > } > $ jshell friendly.jsh > | Welcome to JShell -- Version 23 > | For an introduction type: /help intro > > jshell> var x = input("Loan: ") > Loan: 123 > x ==> 123.0 > > Etc. > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Nov 8 16:04:06 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 8 Nov 2024 10:04:06 -0600 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: On Fri, Nov 8, 2024 at 9:24?AM Ethan McCue wrote: > The *goal* is seems more akin to "improve the experience of learning and > teaching java as a first language". > You make good points - but that kind-of implies that we need to first conduct a pedagogical debate to establish what we think is the "correct" way to teach the Java programming language. Only then would we be able to properly design features to support that goal. Of course I'm putting "correct" in air quotes because that's probably not a resolvable debate. I don't want to have that debate (I'm not a teacher), but I can speak from personal experience. I understand things best when I can focus on learning one self-consistent, well-defined layer of abstraction at a time. Once each layer is mastered, you can then have the "big reveal" of the next layer underneath ("opening the hood)" and then learn how that next layer supports the one above that you already know and understand. Then, rinse & repeat. You can also go in the other direction, i.e., from the bottom up. For example, you might learn digital logic, then counters & flip flops, then adding and shifting, then instruction fetch, then CPU's, then assembly language, then memory management, then operating systems, then C/C++, then Java, etc. etc. The "on-ramp" metaphor instead implies you are learning one big layer of abstraction by gradually reducing the number of "simplifying tricks", so the complexity increases step-by-step. The layer of abstraction you start with is a simplified mirage that keeps changing over time. I'm sure that can work too, but it's a different way of teaching and learning & because it seems weird to me it probably works better in practice :) I'm definitely NOT anti-JEP 495. My point is simply that if the goal is "improve the experience of learning and teaching java as a first language" then that implies some presumptions about how that teaching will be conducted, and it's worth pondering those presumptions. If all the CS teachers of the world are united in saying "this is how we do it" then that's probably sufficient. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Fri Nov 8 16:16:16 2024 From: ethan at mccue.dev (Ethan McCue) Date: Fri, 8 Nov 2024 11:16:16 -0500 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: > but that kind-of implies that we need to first conduct a pedagogical debate to establish what we think is the "correct" way to teach the Java programming language. And maybe also have the hard, most-of-us-are-not-qualified-for-but-oh-well, conversations about how this would actually trickle through the educational industrial complex(es). Same as we wouldn't publish a class without Javadocs, features made for educators without the requisite curriculums and advice on how to apply them feel incomplete. On Fri, Nov 8, 2024 at 11:04?AM Archie Cobbs wrote: > On Fri, Nov 8, 2024 at 9:24?AM Ethan McCue wrote: > >> The *goal* is seems more akin to "improve the experience of learning and >> teaching java as a first language". >> > > You make good points - but that kind-of implies that we need to first > conduct a pedagogical debate to establish what we think is the "correct" > way to teach the Java programming language. Only then would we be able to > properly design features to support that goal. Of course I'm putting > "correct" in air quotes because that's probably not a resolvable debate. > > I don't want to have that debate (I'm not a teacher), but I can speak from > personal experience. I understand things best when I can focus on learning > one self-consistent, well-defined layer of abstraction at a time. Once each > layer is mastered, you can then have the "big reveal" of the next layer > underneath ("opening the hood)" and then learn how that next layer supports > the one above that you already know and understand. Then, rinse & repeat. > > You can also go in the other direction, i.e., from the bottom up. For > example, you might learn digital logic, then counters & flip flops, then > adding and shifting, then instruction fetch, then CPU's, then assembly > language, then memory management, then operating systems, then C/C++, then > Java, etc. etc. > > The "on-ramp" metaphor instead implies you are learning one big layer of > abstraction by gradually reducing the number of "simplifying tricks", so > the complexity increases step-by-step. The layer of abstraction you start > with is a simplified mirage that keeps changing over time. I'm sure that > can work too, but it's a different way of teaching and learning & because > it seems weird to me it probably works better in practice :) > > I'm definitely NOT anti-JEP 495. My point is simply that if the goal is > "improve the experience of learning and teaching java as a first language" > then that implies some presumptions about how that teaching will be > conducted, and it's worth pondering those presumptions. If all the CS > teachers of the world are united in saying "this is how we do it" then > that's probably sufficient. > > -Archie > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kfogel at dawsoncollege.qc.ca Fri Nov 8 17:00:28 2024 From: kfogel at dawsoncollege.qc.ca (Kenneth Fogel) Date: Fri, 8 Nov 2024 17:00:28 +0000 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: Thank you everyone for participating in this conversation. Please continue. Although technically retired, Concordia University allows me to teach one ContEd course of no more than 30 hours each year. That course given online, Introduction to Programming with Java, is coming up in January. I plan to revamp it beginning with some of the concepts from JEP 495 and focus on problem solving more than code construction in at least the first two of ten classes. Fortunately, non-credit ContEd allows me some freedom to experiment with the curriculum. This will be the first time I won?t have to tell my students in the first class to ignore public, static, class, String[] args, and what the heck does System.out mean. I do have to hope for sufficient enrollment. Feel free to send me any advice. Ken -------------- next part -------------- An HTML attachment was scrubbed... URL: From cay.horstmann at gmail.com Sat Nov 9 06:14:30 2024 From: cay.horstmann at gmail.com (Cay Horstmann) Date: Sat, 9 Nov 2024 07:14:30 +0100 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: I am with Stephen here. For effective learning, the gating factor is complexity, not verbosity. The regularity of IO.println wins over the implicit import. Who are we trying to help with "import static java.io.IO.*"? * It's not useful for beginning Java learners. * It might be useful for advocates to say "look, the canonical Hello World in Java is pretty much like the one in Python". Of course, it really isn't, and removing three letters won't change that. * It is mildly useful for people (like me) who use Java for scripting tasks. I am ok with saying "we want Java to be great for tasks of all sizes, and therefore give you unqualified println, print, readln. Even though we know that is yet another fiddly thing for learners to absorb". Everything is a tradeoff. But let's not pretend it makes the language easier to learn. It doesn't. Cheers, Cay On 08/11/2024 10:20, Stephen Colebourne wrote: > On Thu, 7 Nov 2024 at 21:10, Brian Goetz wrote: >> Here's the bar we're envisioning: methods that are automatically imported in this way _effectively become part of the langauge_. The bar for that is very high. > > Making a method like readInt() "effectively part of the language" is > clearly wrong. And therefore obvious why it should be rejected. > But maybe the answer is to take a step back and reconsider whether the > "automatically imported" part of the sentence is a bad choice (which > therefore nullifies the "effectively become part of the language" > claim, effectively allowing readInt and friends). > > The JEP ("Growing a program" section) asks users that migrate from a > simple source file to a normal one to add two imports: > import static java.io.IO.* > import module java.base > > I'm OK with `import module java.base`, but I'm afraid I think `import > static java.io.IO.*` is a really terrible idea. Most production code > should not be using the console at all, it should be using some kind > of logging system. System.out is a hack. IMO, the "growing the > language" approach described in the JEP will end up over time > normalising console output in the minds of developers, with a negative > effect on production Java code. > > Given this, and working backwards (IMO): > - Encouraging `import static java.io.IO.*` is a Really Bad Idea > - Therefore migrating from simple source file should therefore only > require `import module java.base` > - Therefore, simple source files should use IO.println(String), not > just println(String) > - Therefore, more methods can be freely added to IO, like readInt() > > Yes, yes, yes. I know. `IO.println(String)` is more complex than just > `println(String)` for a beginner. But is it really that bad for a > beginner given the risk of normalising console usage? > > Stephen -- Cay S. Horstmann | https://horstmann.com From artyomcool2 at gmail.com Sat Nov 9 10:29:33 2024 From: artyomcool2 at gmail.com (Artyom Drozdov) Date: Sat, 9 Nov 2024 11:29:33 +0100 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: Hello. I would also add an argument to not add auto-import of IO.*. It is easy to explain that IO states for input/output, and as a bonus students will get a full list of available IO-methods from IDE suggests. This may encourage them for self learning and won't introduce very special magic for a very few methods. Regards, Artyom Drozdov ??, 9 ????. 2024??., 11:03 Cay Horstmann : > I am with Stephen here. > > For effective learning, the gating factor is complexity, not verbosity. > The regularity of IO.println wins over the implicit import. > > Who are we trying to help with "import static java.io.IO.*"? > > * It's not useful for beginning Java learners. > * It might be useful for advocates to say "look, the canonical Hello World > in Java is pretty much like the one in Python". Of course, it really isn't, > and removing three letters won't change that. > * It is mildly useful for people (like me) who use Java for scripting > tasks. > > I am ok with saying "we want Java to be great for tasks of all sizes, and > therefore give you unqualified println, print, readln. Even though we know > that is yet another fiddly thing for learners to absorb". Everything is a > tradeoff. > > But let's not pretend it makes the language easier to learn. It doesn't. > > Cheers, > > Cay > > > On 08/11/2024 10:20, Stephen Colebourne wrote: > > On Thu, 7 Nov 2024 at 21:10, Brian Goetz wrote: > >> Here's the bar we're envisioning: methods that are automatically > imported in this way _effectively become part of the langauge_. The bar > for that is very high. > > > > Making a method like readInt() "effectively part of the language" is > > clearly wrong. And therefore obvious why it should be rejected. > > But maybe the answer is to take a step back and reconsider whether the > > "automatically imported" part of the sentence is a bad choice (which > > therefore nullifies the "effectively become part of the language" > > claim, effectively allowing readInt and friends). > > > > The JEP ("Growing a program" section) asks users that migrate from a > > simple source file to a normal one to add two imports: > > import static java.io.IO.* > > import module java.base > > > > I'm OK with `import module java.base`, but I'm afraid I think `import > > static java.io.IO.*` is a really terrible idea. Most production code > > should not be using the console at all, it should be using some kind > > of logging system. System.out is a hack. IMO, the "growing the > > language" approach described in the JEP will end up over time > > normalising console output in the minds of developers, with a negative > > effect on production Java code. > > > > Given this, and working backwards (IMO): > > - Encouraging `import static java.io.IO.*` is a Really Bad Idea > > - Therefore migrating from simple source file should therefore only > > require `import module java.base` > > - Therefore, simple source files should use IO.println(String), not > > just println(String) > > - Therefore, more methods can be freely added to IO, like readInt() > > > > Yes, yes, yes. I know. `IO.println(String)` is more complex than just > > `println(String)` for a beginner. But is it really that bad for a > > beginner given the risk of normalising console usage? > > > > Stephen > > -- > > Cay S. Horstmann | https://horstmann.com > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Sat Nov 9 17:01:46 2024 From: ethan at mccue.dev (Ethan McCue) Date: Sat, 9 Nov 2024 12:01:46 -0500 Subject: Book making use of amber features Message-ID: Hi all, I've shared this with some folks separately, but it's at the point where it's "partially topic complete" enough that I think I can share it more widely. I have been writing a book for covering Java as a first language assuming all the anonymous main class changes have landed. https://javabook.mccue.dev I still need to go back and integrate println, readln, etc. (and obviously go further) I am sharing it here partially because I assume some general interest, but mostly to highlight the section where I transition from single files with anonymous main classes to full classes in their own files. https://javabook.mccue.dev/multi_file_programs If it's going to be how this goes, I think I need advice on how to also include enough pre-work so that import static java.io.IO.*; and import module java.base; won't be a jumpscare. This blurb from my README I think suffices for additional context My primary goals with this are * Get the ordering of topics right. By this I mean that every topic covered should have had its prerequisites covered in the topics previous. While "lesson 1: Inheritance" is clearly wrong in this regard, some things are more subtle. * Be a template for other people. This is a book. Not everyone likes books, some like youtube videos, some like over priced udemy courses, some attend College, etc. Everyone has different learning paths. I hope this to be of use to anyone looking to make a more up to date Java curriculum and hope that the vague order of things (which I consider superior to the content produced with the Java of years' past) is carried through. * Write as if the newest Java wasn't new. It's obvious when a book was written before Java 8 because it always has newer additions with "addendum: brand new stuff in Java 8." But the order language features were introduced is hardly a good order to teach them. You have to pretend that Java 21 has always been *the* Java. Does it really make sense to show terrible C-style switch statements way before switch expressions? * Write as if the words Object Oriented Programming, Functional Programming, etc. didn't exist. While I understand that these all have definitions and are useful concepts to know about, introducing them early seems to lead to either dogma, rejection of said dogma, or some mix thereof. None of them are actually needed to understand the mechanics of and motivation behind what we would call "object oriented" or "functional" techniques. They certainly don't work as justification for adding getters and setters to every class. -------------- next part -------------- An HTML attachment was scrubbed... URL: From johannes.spangenberg at hotmail.de Sat Nov 9 17:32:17 2024 From: johannes.spangenberg at hotmail.de (Johannes Spangenberg) Date: Sat, 9 Nov 2024 18:32:17 +0100 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: >>> Python: loan = input("Loan: ") >> Correct input would be: loan = float(input(...)) > I tested the code that line was from using PyCharm as my IDE and it > ran without any errors or warnings. Have you also verified the result. If you have used "+" or "*" as the operator, then your script might have run successfully, but it might not have done what you have expected. Python 3.10.12 (main, Sep 11 2024, 15:47:36) [GCC 11.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> loan = input("Loan: ") Loan: 5 >>> loan + loan '55' Python has a rather strict (but dynamic) type system. There is no type coercion. If you would use another operator, like "/", you would get an exception. You would also get an exception with "+" if you mix the types (e.g. 100 + "5"). > Dont confuse good languages with js :) In JS, you might actually also have to parse the string, unless you have used an operator which is not defined on strings, in which case type coercion might do the conversion for you. But relying on type?coercion is also often seen as bad practice and nothing I would see as an advantage. For example "5" + "5" is also "55" in JavaScript. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Sun Nov 10 12:08:37 2024 From: ethan at mccue.dev (Ethan McCue) Date: Sun, 10 Nov 2024 07:08:37 -0500 Subject: Book making use of amber features In-Reply-To: References: Message-ID: > Nice! Why not go ahead and change all System.out.println to IO.println? I'll do that once it's either totally set that they are doing plain println and I just need to deal or they make IO.println function. > I read through your transition to multiple files. It isn't clear to me whether up to this point, students always name their program Main.java. Maybe add a note that in general the name of the implicit class is the file name. Noted. I'll reread what I got with that in mind. What I noticed the most is kids with a mega vs code project where they name the files just whatever. I also don't think that the implicit class is supposed to have a defined name, so...hmm. > Just tell students to import module java.base when they use multiple files. I wouldn't get into import static java.io.IO.*. That's a lot of complexity just to save "IO." for three methods. Haven't had a need to yet. Some of this seems to be feedback to those who can actually make these choices. I'll re-add amber dev > Another unrelated point: Why cover arrays before array lists? Lists are more regular. No special syntax, just method calls. And in practice, arrays aren't all that common. Students hate gratuitous choice. When my students asked me when they should use an array and when an array list, I always told them: Use an array list unless your professor, or an API, insists you use an array. Yeah so when I was a student I had the same frustration. Why even bother with arrays? Just show me List! I think what I'm realizing is that I wasn't actually annoyed with arrays, I was shown both arrays and ArrayList before I had enough knowledge to understand why to use which. The rough order now is * Cover arrays first. Let's us do that deeply. All we really care about early on is grinding methods and loops so it's all the same. * Cover ArrayList as useful if "you want basically an array that you can .add to" * (To be done) Cover List and frame List as an abstraction over different kinds of lists including ArrayList + you can turn an array into a List with Arrays.asList + if you don't need to add or set you can use List.of So basically I just think I can build up to a non hand wavey answer to those questions. > Also consider covering maps early on. I am very frustrated with courses that don't teach people maps so 100% going to do it. I have a commented out "build your own hashmap" section just like the "build your own ArrayList" but I might replace it with just "here is a Map/hash map" or... I'm not sure yet. But I don't see a pedagogically sensible way to get them in real early unless I front load Object, generics, etc or start hand waving. On Sun, Nov 10, 2024, 2:53 AM Cay Horstmann wrote: > Nice! Why not go ahead and change all System.out.println to IO.println? > > It's shorter. It's consistent with other methods such as Math.sqrt and > Integer.parseInt. It works in JShell. > > I read through your transition to multiple files. It isn't clear to me > whether up to this point, students always name their program Main.java. > Maybe add a note that in general the name of the implicit class is the file > name. > > Just tell students to import module java.base when they use multiple > files. I wouldn't get into import static java.io.IO.*. That's a lot of > complexity just to save "IO." for three methods. > > Another unrelated point: Why cover arrays before array lists? Lists are > more regular. No special syntax, just method calls. And in practice, arrays > aren't all that common. Students hate gratuitous choice. When my students > asked me when they should use an array and when an array list, I always > told them: Use an array list unless your professor, or an API, insists you > use an array. > > Also consider covering maps early on. > > Cheers, > > Cay > > On 09/11/2024 18:01, Ethan McCue wrote: > > Hi all, > > > > I've shared this with some folks separately, but it's at the point where > it's "partially topic complete" enough that I think I can share it more > widely. > > > > I have been writing a book for covering Java as a first language > assuming all the anonymous main class changes have landed. > > > > https://javabook.mccue.dev > > > > I still need to go back and integrate println, readln, etc. (and > obviously go further) > > > > I am sharing it here partially because I assume some general interest, > but mostly to highlight the section where I transition from single files > with anonymous main classes to full classes in their own files. > > > > https://javabook.mccue.dev/multi_file_programs < > https://javabook.mccue.dev/multi_file_programs> > > > > If it's going to be how this goes, I think I need advice on how to also > include enough pre-work so that import static java.io.IO.*; and import > module java.base; won't be a jumpscare. > > > > This blurb from my README I think suffices for additional context > > > > My primary goals with this are > > > > * Get the ordering of topics right. By this I mean that every topic > covered should have had its prerequisites covered in the topics previous. > While "lesson 1: Inheritance" is clearly wrong in this regard, some things > are more subtle. > > * Be a template for other people. This is a book. Not everyone likes > books, some like youtube videos, some like over priced udemy courses, some > attend College, etc. Everyone has different learning paths. I hope this to > be of use to anyone looking to make a more up to date Java curriculum and > hope that the vague order of things (which I consider superior to the > content > > produced with the Java of years' past) is carried through. > > * Write as if the newest Java wasn't new. It's obvious when a book was > written before Java 8 > > because it always has newer additions with "addendum: brand new stuff in > Java 8." But > > the order language features were introduced is hardly a good order to > teach them. You have > > to pretend that Java 21 has always been *the* Java. Does it really make > sense to show terrible > > C-style switch statements way before switch expressions? > > * Write as if the words Object Oriented Programming, Functional > Programming, etc. didn't exist. > > While I understand that these all have definitions and are useful > concepts to know about, introducing them early seems to lead to either > dogma, rejection of said dogma, or some > > mix thereof. None of them are actually needed to understand the > mechanics of and motivation > > behind what we would call "object oriented" or "functional" techniques. > They certainly don't > > work as justification for adding getters and setters to every class. > > -- > > Cay S. Horstmann | https://horstmann.com > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eirbjo at gmail.com Thu Nov 14 21:47:41 2024 From: eirbjo at gmail.com (=?UTF-8?B?RWlyaWsgQmrDuHJzbsO4cw==?=) Date: Thu, 14 Nov 2024 22:47:41 +0100 Subject: New candidate JEP: 495: Simple Source Files and Instance Main Methods (Fourth Preview) In-Reply-To: References: <20241022153723.5B59777EFDC@eggemoggin.niobe.net> Message-ID: On Thu, Oct 24, 2024 at 4:50?PM Gavin Bierman wrote: > What this JEP is proposing is a new sort of compilation unit. In the draft > spec we call this a ?simple" compilation unit, to contrast with ?ordinary?. > As no human says the phrase ?compilation unit?, but rather everyone > informally speaks of ?source files?, we ended up calling these new sorts of > source files ?simple?. > I guess this ship has sailed, but.. I just watched Gavin's Language Futures talk from Devoxx, and realized I would have preferred the adjective "implicit" used in JEP-477 over "simple". "Implicit" implies a class without an explicit class declaration, whereas "simple" implies everything and nothing at once. Cheers, Eirik, -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Nov 14 23:12:59 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 14 Nov 2024 18:12:59 -0500 Subject: New candidate JEP: 495: Simple Source Files and Instance Main Methods (Fourth Preview) In-Reply-To: References: <20241022153723.5B59777EFDC@eggemoggin.niobe.net> Message-ID: <3526fbbb-20c8-4234-8d60-62586a44a790@oracle.com> As you know, naming things is one of the hardest problems in computer science.? During the course of this project, we tried on several names: unnamed classes, implicit classes, and others.? And there were *extensive* discussions about the pros and cons of each.? Rest assured that the point you made about implicit classes was well understood and represented in the discussion.? In the end, especially given the audience for this feature, the simplicity of "simple" won over the explicitness of "implicit". One of the many reasons naming is hard is that there is never an obviously-right answer; there's only answers that are more or less compelling to more or fewer people.? So even if 90% of the people think it is 90% better, there will still be that 10% who are disappointed.? It's the nature of the game. (And yes, the 23 ship has sailed.) On 11/14/2024 4:47 PM, Eirik Bj?rsn?s wrote: > On Thu, Oct 24, 2024 at 4:50?PM Gavin Bierman > wrote: > > What this JEP is proposing is a new sort of compilation unit. In > the draft spec we call this a ?simple" compilation unit, to > contrast with ?ordinary?. As no human says the phrase ?compilation > unit?, but rather everyone informally speaks of ?source files?, we > ended up calling these new sorts of source files ?simple?. > > > ?I guess this ship has sailed, but.. > > I just watched Gavin's Language Futures talk from Devoxx, and realized > I would have preferred the adjective "implicit" used in JEP-477 over > "simple". "Implicit" implies a class without?an explicit class > declaration, whereas "simple" implies everything?and nothing at once. > > Cheers, > Eirik, -------------- next part -------------- An HTML attachment was scrubbed... URL: From eirbjo at gmail.com Fri Nov 15 07:11:44 2024 From: eirbjo at gmail.com (=?UTF-8?B?RWlyaWsgQmrDuHJzbsO4cw==?=) Date: Fri, 15 Nov 2024 08:11:44 +0100 Subject: New candidate JEP: 495: Simple Source Files and Instance Main Methods (Fourth Preview) In-Reply-To: <3526fbbb-20c8-4234-8d60-62586a44a790@oracle.com> References: <20241022153723.5B59777EFDC@eggemoggin.niobe.net> <3526fbbb-20c8-4234-8d60-62586a44a790@oracle.com> Message-ID: On Fri, Nov 15, 2024 at 12:13?AM Brian Goetz wrote: > As you know, naming things is one of the hardest problems in computer > science. > It's not a simple problem, for sure ;-) > Rest assured that the point you made about implicit classes was well > understood and represented in the discussion. In the end, especially given > the audience for this feature, the simplicity of "simple" won over the > explicitness of "implicit". > Thanks for detailing this, that's good to know. I guess we enter discussions like these with all our previous experiences in mind. I have been in too many situations where seemingly attractive adjectives like "simple" have been used to gloss over a problem which in hindsight should have been tackled head first instead. Good to know that's not the case here. Have a great weekend, may it be a simple one! :) Eirik. -------------- next part -------------- An HTML attachment was scrubbed... URL: From cvmocanu at gmail.com Tue Nov 19 09:49:58 2024 From: cvmocanu at gmail.com (Cristian Mocanu) Date: Tue, 19 Nov 2024 10:49:58 +0100 Subject: JEP-476 module import considered harmful Message-ID: Hello, My name is Cristia Mocanu, and I am a Java developer with almost 20 years of experience. I was made aware of "JEP-476 module import" recently. I strongly recommend making sure this JEP is abandoned and never gets merged into Java. The reason is that a module import shares the same problem with the star import: it makes the code much more difficult to understand without an IDE (e.g. when reviewing a PR on GitHub). The problem with the star import is so bad, that many official code styles, and many teams I worked in, explicitly forbid star imports, making the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport rule). The module import, just like the star import, will have the very bad effect of encouraging people to write code that is difficult to understand. The advantage would be that the VIM guy can type less when writing a Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), but the last time I wrote an import manually was probably 15 years ago - in the real world, we type the class name, and IntelliJ or some other IDE writes the import for us. With fewer words: this JEP has nasty disadvantages, without providing any real world benefit. Kind regards, Cristian -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Nov 19 13:39:29 2024 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 19 Nov 2024 13:39:29 +0000 Subject: JEP-476 module import considered harmful In-Reply-To: References: Message-ID: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Hi. Merely stating that you you believe some feature to be harmful is not helpful feedback, because we already take it as a given that there are many features that some people strongly believe are very useful while others believe just as strongly to be harmful. Such a statement provides no actionable information. What could be helpful is trying to precisely articulate a problem you?ve run into. For example, if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`). Is that the problem you?re referring to? Why does it make reading the code so much more difficult for you? ? Ron > On 19 Nov 2024, at 09:49, Cristian Mocanu wrote: > > Hello, > > My name is Cristia Mocanu, and I am a Java developer with almost 20 years of experience. > > I was made aware of "JEP-476 module import" recently. > > I strongly recommend making sure this JEP is abandoned and never gets merged into Java. > The reason is that a module import shares the same problem with the star import: it makes the code much more difficult to understand without an IDE (e.g. when reviewing a PR on GitHub). > The problem with the star import is so bad, that many official code styles, and many teams I worked in, explicitly forbid star imports, making the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport rule). > > The module import, just like the star import, will have the very bad effect of encouraging people to write code that is difficult to understand. > The advantage would be that the VIM guy can type less when writing a Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), but the last time I wrote an import manually was probably 15 years ago - in the real world, we type the class name, and IntelliJ or some other IDE writes the import for us. > > With fewer words: this JEP has nasty disadvantages, without providing any real world benefit. > > Kind regards, > Cristian From brian.goetz at oracle.com Tue Nov 19 14:07:03 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 19 Nov 2024 14:07:03 +0000 Subject: JEP-476 module import considered harmful In-Reply-To: References: Message-ID: <0B0A9DE8-70FC-4284-AFEF-410D9C3508A1@oracle.com> This mail would have benefited from some additional thought before writing (and especially before turning the ?this is a disaster? knob to 11 or trotting out the overused trope of ?considered harmful?.) Yes, OF COURSE some people dislike star imports, and with some vaiid reasons, including the ones you raise. Did you think that there was even a tiny chance that we were not aware of this? Someone of your extensive experience should be able to go through the thought process of: - I think this is a bad idea - But, smart people who think deeply about language evolution and its impact on the world of code through about it for a long time and came to a different conclusion - Perhaps there is more to it than I think, I should inquire before writing such a jumping-to-disaster mail. (I think also you misunderstand the motivation for why most style guides disrecommend star imports; it is not primarily readability, as no one in the real world actually reads imports, and questions of ?where is this class? are well handled by IDEs; the primary motivation is usually stability, which comes from its explicitness.) So yes, all of these arguments were well understood from the outset, and were discussed during the design process. What is missing from the ?but of course everyone knows this? analysis is that the concerns over star imports have a context: code that has reached the mature phase of its lifecycle, and for which stability has become the most important consideration. Within that context, these concerns are valid, but this context does not remotely cover the whole experience of writing Java code. Some of the countervailing considerations included: - Single-class imports have tradeoffs that go beyond the mere ?typing less?. It also means _reading less_; not having to grovel through hundreds of import lines to get a sense of the dependencies. Java developers complain about this as an aspect of Java?s ?boilerplate?. - For new code (or new developers), the fussiness of imports is an impediment to getting things done. The arguments about stability do not apply here, and enforcing them ?for your own good? seems almost kind of mean. - For codebases that prefer single imports, automated refactoring is widely available, so starting out with module or package imports is not creating a long-term problem. - Expressing a dependency on a package or module is a more abstract statement than a single class. - Expressing a dependency on a module is _intrinsically more sensible_ than expressing it on all (or most) of the packages in a module. Users who want to ?use Jackson? have to grovel through tutorials to figure out which packages to import. - Having modules be the unit of dependency, but packages be the unit of import, is an accidental asymmetry that should be corrected. Most modules (java.base notwithstanding) have a single purpose an a single ?import module com.foo.WhizzyJsonParser? is a clear statement of semantic intent. In fact, this is sometimes a _clearer_ statement of intent than importing all the individual classes. - Even within the context of stable, mature code, dealing with emergent name clashes requires only trivial remediation. So even such codebases may prefer the more abstract expression of module dependency because the costs of mitigating the resulting instability is low. - The preference of some for star imports is a preference; this isn?t really a good argument for ?so no one should be allowed to code like this" Which is to say, _sometimes_ the benefits of star imports do not exceed their costs. And in those cases, you are correct, you should not use them! But that doesn?t make them intrinsically bad, or mean that we should force single-import on all users because they are not ideal in all contexts. Finally, one more point on the sky-is-falling rhetoric: it?s not helpful. We saw this with local variable type inference (var); many developers catastrophized loudly that this would destroy the readability of Java code, but that didn?t happen. On Nov 19, 2024, at 4:49 AM, Cristian Mocanu > wrote: Hello, My name is Cristia Mocanu, and I am a Java developer with almost 20 years of experience. I was made aware of "JEP-476 module import" recently. I strongly recommend making sure this JEP is abandoned and never gets merged into Java. The reason is that a module import shares the same problem with the star import: it makes the code much more difficult to understand without an IDE (e.g. when reviewing a PR on GitHub). The problem with the star import is so bad, that many official code styles, and many teams I worked in, explicitly forbid star imports, making the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport rule). The module import, just like the star import, will have the very bad effect of encouraging people to write code that is difficult to understand. The advantage would be that the VIM guy can type less when writing a Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), but the last time I wrote an import manually was probably 15 years ago - in the real world, we type the class name, and IntelliJ or some other IDE writes the import for us. With fewer words: this JEP has nasty disadvantages, without providing any real world benefit. Kind regards, Cristian -------------- next part -------------- An HTML attachment was scrubbed... URL: From cvmocanu at gmail.com Tue Nov 19 13:52:01 2024 From: cvmocanu at gmail.com (Cristian Mocanu) Date: Tue, 19 Nov 2024 14:52:01 +0100 Subject: JEP-476 module import considered harmful In-Reply-To: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: Hi Ron, Sorry, I incorrectly assumed that the problem is well known. You stated it almost correctly: "if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`)". To make the problem clearer - if I see class X, and I'm not sure from which module it comes from, I need to manually open the source code of all imported modules, and check if they contain the class. Or, much simpler, open the Java file in an IDE, and check there from what module it comes. You're referring to documentation, but I would like to broaden the problem a bit. To lookup documentation, I would just use my IDE, which would "know" which module X is coming from. The basic problem is that it's no longer obvious where X is coming from, only by looking at the source code. For example, if you see `StringUtils`, you can't tell whether it comes from Spring, or Apache Commons Lang, or Micrometer, or Apache Commons Codec, or Logback Logstash Encoder, or some other library - it can be any one of the module imports. Knowing that code is being read many time more often than written, I don't think it makes sense to optimize writing an import by hand (which no one does anyway - the IDE writes it for us) to the detriment of introducing confusion when reading the code outside an IDE (like a GitHub PR review). Kind regards, Cristian On Tue, 19 Nov 2024 at 14:39, Ron Pressler wrote: > Hi. > > Merely stating that you you believe some feature to be harmful is not > helpful feedback, because we already take it as a given that there are many > features that some people strongly believe are very useful while others > believe just as strongly to be harmful. Such a statement provides no > actionable information. > > What could be helpful is trying to precisely articulate a problem you?ve > run into. For example, if a file has `import module a; import module b;` > and it uses the type `X`, you would need to search both `a` and `b` to find > the documentation for `X` when reading the code, rather than just one of > them (if we had, say, import X from module a`). Is that the problem you?re > referring to? Why does it make reading the code so much more difficult for > you? > > ? Ron > > > On 19 Nov 2024, at 09:49, Cristian Mocanu wrote: > > > > Hello, > > > > My name is Cristia Mocanu, and I am a Java developer with almost 20 > years of experience. > > > > I was made aware of "JEP-476 module import" recently. > > > > I strongly recommend making sure this JEP is abandoned and never gets > merged into Java. > > The reason is that a module import shares the same problem with the star > import: it makes the code much more difficult to understand without an IDE > (e.g. when reviewing a PR on GitHub). > > The problem with the star import is so bad, that many official code > styles, and many teams I worked in, explicitly forbid star imports, making > the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport > rule). > > > > The module import, just like the star import, will have the very bad > effect of encouraging people to write code that is difficult to understand. > > The advantage would be that the VIM guy can type less when writing a > Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), > but the last time I wrote an import manually was probably 15 years ago - in > the real world, we type the class name, and IntelliJ or some other IDE > writes the import for us. > > > > With fewer words: this JEP has nasty disadvantages, without providing > any real world benefit. > > > > Kind regards, > > Cristian > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 19 14:22:47 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 19 Nov 2024 14:22:47 +0000 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: <34FA2CE9-1A8E-4C7C-8ED3-97BDEB4EF25A@oracle.com> To illustrate that your view about star imports is merely one of many, have a look at https://stackoverflow.com/questions/147454/why-is-using-a-wild-card-with-a-java-import-statement-bad Summarizing the answers to this q (considering only answers with more than a few upvotes): - Star imports are bad because they clutter the namespace with stuff you don?t need, and might conflict - I like single imports because they feel more clear - Star imports are bad because there are conflicts like java.util.List vs java.awt.List - I like star imports because they express the dependencies more abstractly - Even if there are no conflicts now, there could be conflicts arising later, which means star imports are less stable - I like star imports because they make the code more compact Which is to say, opinions are all over the map, and even among star-import haters, the reasons are mostly not about readability, but about conflict management and stability. On Nov 19, 2024, at 8:52 AM, Cristian Mocanu > wrote: Hi Ron, Sorry, I incorrectly assumed that the problem is well known. You stated it almost correctly: "if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`)". To make the problem clearer - if I see class X, and I'm not sure from which module it comes from, I need to manually open the source code of all imported modules, and check if they contain the class. Or, much simpler, open the Java file in an IDE, and check there from what module it comes. You're referring to documentation, but I would like to broaden the problem a bit. To lookup documentation, I would just use my IDE, which would "know" which module X is coming from. The basic problem is that it's no longer obvious where X is coming from, only by looking at the source code. For example, if you see `StringUtils`, you can't tell whether it comes from Spring, or Apache Commons Lang, or Micrometer, or Apache Commons Codec, or Logback Logstash Encoder, or some other library - it can be any one of the module imports. Knowing that code is being read many time more often than written, I don't think it makes sense to optimize writing an import by hand (which no one does anyway - the IDE writes it for us) to the detriment of introducing confusion when reading the code outside an IDE (like a GitHub PR review). Kind regards, Cristian On Tue, 19 Nov 2024 at 14:39, Ron Pressler > wrote: Hi. Merely stating that you you believe some feature to be harmful is not helpful feedback, because we already take it as a given that there are many features that some people strongly believe are very useful while others believe just as strongly to be harmful. Such a statement provides no actionable information. What could be helpful is trying to precisely articulate a problem you?ve run into. For example, if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`). Is that the problem you?re referring to? Why does it make reading the code so much more difficult for you? ? Ron > On 19 Nov 2024, at 09:49, Cristian Mocanu > wrote: > > Hello, > > My name is Cristia Mocanu, and I am a Java developer with almost 20 years of experience. > > I was made aware of "JEP-476 module import" recently. > > I strongly recommend making sure this JEP is abandoned and never gets merged into Java. > The reason is that a module import shares the same problem with the star import: it makes the code much more difficult to understand without an IDE (e.g. when reviewing a PR on GitHub). > The problem with the star import is so bad, that many official code styles, and many teams I worked in, explicitly forbid star imports, making the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport rule). > > The module import, just like the star import, will have the very bad effect of encouraging people to write code that is difficult to understand. > The advantage would be that the VIM guy can type less when writing a Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), but the last time I wrote an import manually was probably 15 years ago - in the real world, we type the class name, and IntelliJ or some other IDE writes the import for us. > > With fewer words: this JEP has nasty disadvantages, without providing any real world benefit. > > Kind regards, > Cristian -------------- next part -------------- An HTML attachment was scrubbed... URL: From pedro.lamarao at prodist.com.br Tue Nov 19 14:37:30 2024 From: pedro.lamarao at prodist.com.br (=?UTF-8?Q?Pedro_Lamar=C3=A3o?=) Date: Tue, 19 Nov 2024 11:37:30 -0300 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: Em ter., 19 de nov. de 2024 ?s 11:09, Cristian Mocanu escreveu: > Knowing that code is being read many time more often than written, I don't > think it makes sense to optimize writing an import by hand (which no one > does anyway - the IDE writes it for us) to the detriment of introducing > confusion when reading the code outside an IDE (like a GitHub PR review). > Optimizing Java for GitWeb-like readers does not seem to me the way forward. GitWeb-like products are themselves moving toward "AI powered web IDE" status. -- Pedro Lamar?o -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Nov 19 15:12:01 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 19 Nov 2024 09:12:01 -0600 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: I'd like to throw out a note of hope...? :) I think there is a reasonable debate about whether star imports are "good" or "bad" but from the POV of language design that debate is made almost entirely moot thanks to the robust ecosystem of style checkers. In other words, this is, in effect, just a policy decision that your particular organization can enforce, just like other style preferences such as whether to allow switch case fallthrough, whether to require curly braces around a one-line if/else branch, etc. If you don't like star imports, then have them cause your build to fail. Problem solved! In this case, just because (a) Java enables a coding practice you don't like doesn't mean (b) you have to tolerate it in your own organization. The (a) and (b) are two separate issues with two separate debates and two separate resolutions. -Archie On Tue, Nov 19, 2024 at 8:38?AM Pedro Lamar?o wrote: > Em ter., 19 de nov. de 2024 ?s 11:09, Cristian Mocanu > escreveu: > > >> Knowing that code is being read many time more often than written, I >> don't think it makes sense to optimize writing an import by hand (which no >> one does anyway - the IDE writes it for us) to the detriment of introducing >> confusion when reading the code outside an IDE (like a GitHub PR review). >> > > Optimizing Java for GitWeb-like readers does not seem to me the way > forward. > GitWeb-like products are themselves moving toward "AI powered web IDE" > status. > > -- > Pedro Lamar?o > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From pavel.rappo at oracle.com Tue Nov 19 15:12:48 2024 From: pavel.rappo at oracle.com (Pavel Rappo) Date: Tue, 19 Nov 2024 15:12:48 +0000 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: <823A278E-AEE6-4C35-934B-D2B7F22C6821@oracle.com> > On 19 Nov 2024, at 14:37, Pedro Lamar?o wrote: > > Em ter., 19 de nov. de 2024 ?s 11:09, Cristian Mocanu escreveu: > Knowing that code is being read many time more often than written, I don't think it makes sense to optimize writing an import by hand (which no one does anyway - the IDE writes it for us) to the detriment of introducing confusion when reading the code outside an IDE (like a GitHub PR review). > > Optimizing Java for GitWeb-like readers does not seem to me the way forward. > GitWeb-like products are themselves moving toward "AI powered web IDE" status. I'll comment specifically to your comment, and not to the original post or follow-up comments from Ron or Brian. It's not optimising Java for Web/IDE things. It's the other way around: it's making sure one does __not__ need those or any other external things to work with Java. Again, not for this case, but in general: imagine introducing a language feature so complicated and non-intuitive that you'd __need__ IDE support to work with it. Example: no matter how genius text blocks (JEP 378) are, which they certainly are, I feel like I at times I need IDE support to figure out or remind me how to figure out incidental whitespace. -Pavel From cvmocanu at gmail.com Tue Nov 19 15:17:51 2024 From: cvmocanu at gmail.com (Cristian Mocanu) Date: Tue, 19 Nov 2024 16:17:51 +0100 Subject: JEP-476 module import considered harmful In-Reply-To: <0B0A9DE8-70FC-4284-AFEF-410D9C3508A1@oracle.com> References: <0B0A9DE8-70FC-4284-AFEF-410D9C3508A1@oracle.com> Message-ID: Hi Brian, I apologize for appearing to be attacking the persons behind the proposal. That was not my intention. I intended simply to give my reasons why I think import modules are a bad idea. To engage with the idea - not with the people :) > code that has reached the mature phase of its lifecycle, and for which stability has become the most important consideration I can see how sometimes proof of concepts are being abandoned - and maybe this happens more when developing Java itself or a library. But in most projects, proof of concepts become the main product, which makes it necessary to write quality code even for a proof of concept. > - Single-class imports have tradeoffs that go beyond the mere ?typing less?. It also means _reading less_; not having to grovel through hundreds of import lines to get a sense of the dependencies. Java developers complain about this as an aspect of Java?s ?boilerplate?. You can always read less - just skip reading the dependencies. Moreover, to avoid SCM conflicts, you should force a build failure if the imports are not sorted. And if the dependencies are sorted, it's easy to see the common prefix. Having (sorted) individual classes in the imports is even better to give you a sense of the dependencies - you get a sense of how much a particular module is used. Regarding boilerplate: junior developer don't understand that there are 2 types of boilerplate: * boilerplate that doesn't add to the readability of the code * boilerplate that makes the code clearer (like extracting local variables, just to have an explanatory name for a part of an expression) The first type should be avoided, the second type is desirable. > - For new code (or new developers), the fussiness of imports is an impediment to getting things done. I fail to understand the difference between (1) letting the IDE write the import for me for new code, vs (2) letting the IDE write the import for me for code that needs to be kept stable. > - Expressing a dependency on a package or module is a more abstract statement than a single class. > - Expressing a dependency on a module is _intrinsically more sensible_ than expressing it on all (or most) of the packages in a module. We already have a place for that: Gradle or Maven (or the `module-info.java` for people using modules) > Users who want to ?use Jackson? have to grovel through tutorials to figure out which packages to import. In 20 years of programming, I **never** had to look up what package to import. Again - developers don't write import statements by hand - the IDE does it for us. I just type `ObjectMapper` and the IDE will write the correct import (or let me choose, if multiple classes with that name are available in the classpath). > - Having modules be the unit of dependency, but packages be the unit of import, is an accidental asymmetry that should be corrected. I, and many of the devs I worked with, use packages as a replacement for namespaces from C# - it's a total mess to have a lot of classes inside a single package. > Most modules (java.base notwithstanding) have a single purpose an a single ?import module com.foo.WhizzyJsonParser? is a clear statement of semantic intent. In fact, this is sometimes a _clearer_ statement of intent than importing all the individual classes. I agree - but we already have `module-info.java` for this. > - The preference of some for star imports is a preference; this isn?t really a good argument for ?so no one should be allowed to code like this" I didn't merely state that star imports are bad (which would be a mere opinion) - I gave arguments to back my assertion: they introduce confusion. Maybe you think there's no confusion being introduced, or that the confusion is justifiable. I guess sometimes we have to agree to disagree. > Which is to say, _sometimes_ the benefits of star imports do not exceed their costs. And in those cases, you are correct, you should not use them! But that doesn?t make them intrinsically bad, or mean that we should force single-import on all users because they are not ideal in all contexts. Do you have **any** case in particular where the benefits exceed the added confusion ? > Finally, one more point on the sky-is-falling rhetoric: it?s not helpful. We saw this with local variable type inference (var); many developers catastrophized loudly that this would destroy the readability of Java code, but that didn?t happen. I'm sorry for coming up this strong. I should have just stated the arguments against it, and leave it at that. I'm glad you brought up `var`, though. In my experience, `var` makes the code more difficult to understand much more often than the cases where it clears up things. In other words, we have a feature that makes the code more complicated (harder to understand) more often than not. I wouldn't have added such a feature to the language, just to satisfy people used to non-statically-typed languages. Kind regards, Cristian On Tue, 19 Nov 2024 at 15:07, Brian Goetz wrote: > This mail would have benefited from some additional thought before writing > (and especially before turning the ?this is a disaster? knob to 11 or > trotting out the overused trope of ?considered harmful?.) > > Yes, OF COURSE some people dislike star imports, and with some vaiid > reasons, including the ones you raise. Did you think that there was even a > tiny chance that we were not aware of this? Someone of your extensive > experience should be able to go through the thought process of: > > - I think this is a bad idea > - But, smart people who think deeply about language evolution and its > impact on the world of code through about it for a long time and came to a > different conclusion > - Perhaps there is more to it than I think, I should inquire > > before writing such a jumping-to-disaster mail. (I think also you > misunderstand the motivation for why most style guides disrecommend star > imports; it is not primarily readability, as no one in the real world > actually reads imports, and questions of ?where is this class? are well > handled by IDEs; the primary motivation is usually stability, which comes > from its explicitness.) > > > So yes, all of these arguments were well understood from the outset, and > were discussed during the design process. What is missing from the ?but of > course everyone knows this? analysis is that the concerns over star imports > have a context: code that has reached the mature phase of its lifecycle, > and for which stability has become the most important consideration. > Within that context, these concerns are valid, but this context does not > remotely cover the whole experience of writing Java code. > > Some of the countervailing considerations included: > > - Single-class imports have tradeoffs that go beyond the mere ?typing > less?. It also means _reading less_; not having to grovel through hundreds > of import lines to get a sense of the dependencies. Java developers > complain about this as an aspect of Java?s ?boilerplate?. > - For new code (or new developers), the fussiness of imports is an > impediment to getting things done. The arguments about stability do not > apply here, and enforcing them ?for your own good? seems almost kind of > mean. > - For codebases that prefer single imports, automated refactoring is > widely available, so starting out with module or package imports is not > creating a long-term problem. > - Expressing a dependency on a package or module is a more abstract > statement than a single class. > - Expressing a dependency on a module is _intrinsically more sensible_ > than expressing it on all (or most) of the packages in a module. Users who > want to ?use Jackson? have to grovel through tutorials to figure out which > packages to import. > - Having modules be the unit of dependency, but packages be the unit of > import, is an accidental asymmetry that should be corrected. Most modules > (java.base notwithstanding) have a single purpose an a single ?import > module com.foo.WhizzyJsonParser? is a clear statement of semantic intent. > In fact, this is sometimes a _clearer_ statement of intent than importing > all the individual classes. > - Even within the context of stable, mature code, dealing with emergent > name clashes requires only trivial remediation. So even such codebases may > prefer the more abstract expression of module dependency because the costs > of mitigating the resulting instability is low. > - The preference of some for star imports is a preference; this isn?t > really a good argument for ?so no one should be allowed to code like this" > > Which is to say, _sometimes_ the benefits of star imports do not exceed > their costs. And in those cases, you are correct, you should not use > them! But that doesn?t make them intrinsically bad, or mean that we should > force single-import on all users because they are not ideal in all > contexts. > > Finally, one more point on the sky-is-falling rhetoric: it?s not helpful. > We saw this with local variable type inference (var); many developers > catastrophized loudly that this would destroy the readability of Java code, > but that didn?t happen. > > > > > > On Nov 19, 2024, at 4:49 AM, Cristian Mocanu wrote: > > Hello, > > My name is Cristia Mocanu, and I am a Java developer with almost 20 years > of experience. > > I was made aware of "JEP-476 module import" recently. > > I strongly recommend making sure this JEP is abandoned and never gets > merged into Java. > The reason is that a module import shares the same problem with the star > import: it makes the code much more difficult to understand without an IDE > (e.g. when reviewing a PR on GitHub). > The problem with the star import is so bad, that many official code > styles, and many teams I worked in, explicitly forbid star imports, making > the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport > rule). > > The module import, just like the star import, will have the very bad > effect of encouraging people to write code that is difficult to understand. > The advantage would be that the VIM guy can type less when writing a Java > file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), but > the last time I wrote an import manually was probably 15 years ago - in the > real world, we type the class name, and IntelliJ or some other IDE writes > the import for us. > > With fewer words: this JEP has nasty disadvantages, without providing any > real world benefit. > > Kind regards, > Cristian > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From cvmocanu at gmail.com Tue Nov 19 15:23:45 2024 From: cvmocanu at gmail.com (Cristian Mocanu) Date: Tue, 19 Nov 2024 16:23:45 +0100 Subject: JEP-476 module import considered harmful In-Reply-To: <34FA2CE9-1A8E-4C7C-8ED3-97BDEB4EF25A@oracle.com> References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> <34FA2CE9-1A8E-4C7C-8ED3-97BDEB4EF25A@oracle.com> Message-ID: Hi Brian, > - I like single imports because they feel more clear Invalid. How does knowing less feel clearer? Less code is not always easier to understand. > - I like star imports because they express the dependencies more abstractly How is this a benefit worth having more confusing code? What is the exact benefit? > - I like star imports because they make the code more compact Totally invalid! Junior developers don't understand that more compact code comes in 2 kinds: * the kind that makes the code easier to understand * the kind that makes the code harder to understand Having to write the types when declaring variables or fields makes the code less compact, but more readable at the same time. > Which is to say, opinions are all over the map Agreed, but not all opinions are valid :) Kind regards, Cristian On Tue, 19 Nov 2024 at 15:22, Brian Goetz wrote: > To illustrate that your view about star imports is merely one of many, > have a look at > > > https://stackoverflow.com/questions/147454/why-is-using-a-wild-card-with-a-java-import-statement-bad > > Summarizing the answers to this q (considering only answers with more than > a few upvotes): > > - Star imports are bad because they clutter the namespace with stuff you > don?t need, and might conflict > - I like single imports because they feel more clear > - Star imports are bad because there are conflicts like java.util.List vs > java.awt.List > - I like star imports because they express the dependencies more > abstractly > - Even if there are no conflicts now, there could be conflicts arising > later, which means star imports are less stable > - I like star imports because they make the code more compact > > Which is to say, opinions are all over the map, and even among star-import > haters, the reasons are mostly not about readability, but about conflict > management and stability. > > On Nov 19, 2024, at 8:52 AM, Cristian Mocanu wrote: > > Hi Ron, > > Sorry, I incorrectly assumed that the problem is well known. > > You stated it almost correctly: "if a file has `import module a; import > module b;` and it uses the type `X`, you would need to search both `a` and > `b` to find the documentation for `X` when reading the code, rather than > just one of them (if we had, say, import X from module a`)". > > To make the problem clearer - if I see class X, and I'm not sure from > which module it comes from, I need to manually open the source code of all > imported modules, and check if they contain the class. > Or, much simpler, open the Java file in an IDE, and check there from what > module it comes. > > You're referring to documentation, but I would like to broaden the problem > a bit. > To lookup documentation, I would just use my IDE, which would "know" which > module X is coming from. > > The basic problem is that it's no longer obvious where X is coming from, > only by looking at the source code. For example, if you see `StringUtils`, > you can't tell whether it comes from Spring, or Apache Commons Lang, or > Micrometer, or Apache Commons Codec, or Logback Logstash Encoder, or some > other library - it can be any one of the module imports. > > Knowing that code is being read many time more often than written, I don't > think it makes sense to optimize writing an import by hand (which no one > does anyway - the IDE writes it for us) to the detriment of introducing > confusion when reading the code outside an IDE (like a GitHub PR review). > > Kind regards, > Cristian > > On Tue, 19 Nov 2024 at 14:39, Ron Pressler > wrote: > >> Hi. >> >> Merely stating that you you believe some feature to be harmful is not >> helpful feedback, because we already take it as a given that there are many >> features that some people strongly believe are very useful while others >> believe just as strongly to be harmful. Such a statement provides no >> actionable information. >> >> What could be helpful is trying to precisely articulate a problem you?ve >> run into. For example, if a file has `import module a; import module b;` >> and it uses the type `X`, you would need to search both `a` and `b` to find >> the documentation for `X` when reading the code, rather than just one of >> them (if we had, say, import X from module a`). Is that the problem you?re >> referring to? Why does it make reading the code so much more difficult for >> you? >> >> ? Ron >> >> > On 19 Nov 2024, at 09:49, Cristian Mocanu wrote: >> > >> > Hello, >> > >> > My name is Cristia Mocanu, and I am a Java developer with almost 20 >> years of experience. >> > >> > I was made aware of "JEP-476 module import" recently. >> > >> > I strongly recommend making sure this JEP is abandoned and never gets >> merged into Java. >> > The reason is that a module import shares the same problem with the >> star import: it makes the code much more difficult to understand without an >> IDE (e.g. when reviewing a PR on GitHub). >> > The problem with the star import is so bad, that many official code >> styles, and many teams I worked in, explicitly forbid star imports, making >> the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport >> rule). >> > >> > The module import, just like the star import, will have the very bad >> effect of encouraging people to write code that is difficult to understand. >> > The advantage would be that the VIM guy can type less when writing a >> Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), >> but the last time I wrote an import manually was probably 15 years ago - in >> the real world, we type the class name, and IntelliJ or some other IDE >> writes the import for us. >> > >> > With fewer words: this JEP has nasty disadvantages, without providing >> any real world benefit. >> > >> > Kind regards, >> > Cristian >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 19 15:26:58 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 19 Nov 2024 15:26:58 +0000 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: <3DEEF0A0-B2DC-4A94-8005-10C3E5B3B50E@oracle.com> Indeed, Archie. There are a lot of folks out there that would like the language to be in the business of enforcing stylistic choices, but this is frequently a misguided motivation. If you want to impose a stylistic preference on your own code, use a style-checking tool. On Nov 19, 2024, at 10:12 AM, Archie Cobbs > wrote: I'd like to throw out a note of hope...? :) I think there is a reasonable debate about whether star imports are "good" or "bad" but from the POV of language design that debate is made almost entirely moot thanks to the robust ecosystem of style checkers. In other words, this is, in effect, just a policy decision that your particular organization can enforce, just like other style preferences such as whether to allow switch case fallthrough, whether to require curly braces around a one-line if/else branch, etc. If you don't like star imports, then have them cause your build to fail. Problem solved! In this case, just because (a) Java enables a coding practice you don't like doesn't mean (b) you have to tolerate it in your own organization. The (a) and (b) are two separate issues with two separate debates and two separate resolutions. -Archie On Tue, Nov 19, 2024 at 8:38?AM Pedro Lamar?o > wrote: Em ter., 19 de nov. de 2024 ?s 11:09, Cristian Mocanu > escreveu: Knowing that code is being read many time more often than written, I don't think it makes sense to optimize writing an import by hand (which no one does anyway - the IDE writes it for us) to the detriment of introducing confusion when reading the code outside an IDE (like a GitHub PR review). Optimizing Java for GitWeb-like readers does not seem to me the way forward. GitWeb-like products are themselves moving toward "AI powered web IDE" status. -- Pedro Lamar?o -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Tue Nov 19 15:32:02 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Tue, 19 Nov 2024 09:32:02 -0600 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: Hi Cristian, On Tue, Nov 19, 2024 at 9:28?AM Cristian Mocanu wrote: > > In this case, just because (a) Java enables a coding practice you don't > like doesn't mean (b) you have to tolerate it in your own organization > Indeed, it can seem that way, but in reality it doesn't work well. In > reality, I have to maintain code written by devs who don't work at the > company anymore for a long time. They left a mess of a code behind, and in > the case of undesirable language features, they did it because they could. > I have very real sympathy for that situation - based on past experience! Unfortunately the world is chock full of human/organizational problems that technology will never fix... -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 19 15:35:04 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 19 Nov 2024 15:35:04 +0000 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> <34FA2CE9-1A8E-4C7C-8ED3-97BDEB4EF25A@oracle.com> Message-ID: <0BE70C77-093C-4D8E-9C21-8E9A1A90B2A6@oracle.com> I think you missed the point, though. The point was not to reopen the debate; it was to help you realize that your opinion is just one of many, and the fact that you disagree doesn?t mean that the others are wrong (and that we should design the language around deterring their wrongness.). You seem very quick to dismiss opinions other than your own as ?invalid? and therefore not worthy of learning from. I would suggest trying to acquire the ability to look at your own (seemingly very strong) opinions with the same degree of skepticism. Otherwise, you will find a very difficult time convincing anyone to agree with you, which you will probably find frustrating. As a reminder, amber-dev is not the feature design list; if you are hoping to reopen the discussion, you have to come with arguments or data that are new. I hope that the discussion so far has convinced you that all of this has been said before, and considered appropriately. (And ?they didn?t agree with me? the same as ?this was not considered appropriately.?) On Nov 19, 2024, at 10:23 AM, Cristian Mocanu > wrote: Hi Brian, > - I like single imports because they feel more clear Invalid. How does knowing less feel clearer? Less code is not always easier to understand. > - I like star imports because they express the dependencies more abstractly How is this a benefit worth having more confusing code? What is the exact benefit? > - I like star imports because they make the code more compact Totally invalid! Junior developers don't understand that more compact code comes in 2 kinds: * the kind that makes the code easier to understand * the kind that makes the code harder to understand Having to write the types when declaring variables or fields makes the code less compact, but more readable at the same time. > Which is to say, opinions are all over the map Agreed, but not all opinions are valid :) Kind regards, Cristian On Tue, 19 Nov 2024 at 15:22, Brian Goetz > wrote: To illustrate that your view about star imports is merely one of many, have a look at https://stackoverflow.com/questions/147454/why-is-using-a-wild-card-with-a-java-import-statement-bad Summarizing the answers to this q (considering only answers with more than a few upvotes): - Star imports are bad because they clutter the namespace with stuff you don?t need, and might conflict - I like single imports because they feel more clear - Star imports are bad because there are conflicts like java.util.List vs java.awt.List - I like star imports because they express the dependencies more abstractly - Even if there are no conflicts now, there could be conflicts arising later, which means star imports are less stable - I like star imports because they make the code more compact Which is to say, opinions are all over the map, and even among star-import haters, the reasons are mostly not about readability, but about conflict management and stability. On Nov 19, 2024, at 8:52 AM, Cristian Mocanu > wrote: Hi Ron, Sorry, I incorrectly assumed that the problem is well known. You stated it almost correctly: "if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`)". To make the problem clearer - if I see class X, and I'm not sure from which module it comes from, I need to manually open the source code of all imported modules, and check if they contain the class. Or, much simpler, open the Java file in an IDE, and check there from what module it comes. You're referring to documentation, but I would like to broaden the problem a bit. To lookup documentation, I would just use my IDE, which would "know" which module X is coming from. The basic problem is that it's no longer obvious where X is coming from, only by looking at the source code. For example, if you see `StringUtils`, you can't tell whether it comes from Spring, or Apache Commons Lang, or Micrometer, or Apache Commons Codec, or Logback Logstash Encoder, or some other library - it can be any one of the module imports. Knowing that code is being read many time more often than written, I don't think it makes sense to optimize writing an import by hand (which no one does anyway - the IDE writes it for us) to the detriment of introducing confusion when reading the code outside an IDE (like a GitHub PR review). Kind regards, Cristian On Tue, 19 Nov 2024 at 14:39, Ron Pressler > wrote: Hi. Merely stating that you you believe some feature to be harmful is not helpful feedback, because we already take it as a given that there are many features that some people strongly believe are very useful while others believe just as strongly to be harmful. Such a statement provides no actionable information. What could be helpful is trying to precisely articulate a problem you?ve run into. For example, if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`). Is that the problem you?re referring to? Why does it make reading the code so much more difficult for you? ? Ron > On 19 Nov 2024, at 09:49, Cristian Mocanu > wrote: > > Hello, > > My name is Cristia Mocanu, and I am a Java developer with almost 20 years of experience. > > I was made aware of "JEP-476 module import" recently. > > I strongly recommend making sure this JEP is abandoned and never gets merged into Java. > The reason is that a module import shares the same problem with the star import: it makes the code much more difficult to understand without an IDE (e.g. when reviewing a PR on GitHub). > The problem with the star import is so bad, that many official code styles, and many teams I worked in, explicitly forbid star imports, making the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport rule). > > The module import, just like the star import, will have the very bad effect of encouraging people to write code that is difficult to understand. > The advantage would be that the VIM guy can type less when writing a Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), but the last time I wrote an import manually was probably 15 years ago - in the real world, we type the class name, and IntelliJ or some other IDE writes the import for us. > > With fewer words: this JEP has nasty disadvantages, without providing any real world benefit. > > Kind regards, > Cristian -------------- next part -------------- An HTML attachment was scrubbed... URL: From cvmocanu at gmail.com Tue Nov 19 15:28:15 2024 From: cvmocanu at gmail.com (Cristian Mocanu) Date: Tue, 19 Nov 2024 16:28:15 +0100 Subject: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: Hi Archie, Thanks for your reply. > In this case, just because (a) Java enables a coding practice you don't like doesn't mean (b) you have to tolerate it in your own organization Indeed, it can seem that way, but in reality it doesn't work well. In reality, I have to maintain code written by devs who don't work at the company anymore for a long time. They left a mess of a code behind, and in the case of undesirable language features, they did it because they could. Kind regards, Cristian On Tue, 19 Nov 2024 at 16:12, Archie Cobbs wrote: > I'd like to throw out a note of hope...? :) > > I think there is a reasonable debate about whether star imports are "good" > or "bad" but from the POV of language design that debate is made almost > entirely moot thanks to the robust ecosystem of style checkers. > > In other words, this is, in effect, just a policy decision that your > particular organization can enforce, just like other style preferences such > as whether to allow switch case fallthrough, whether to require curly > braces around a one-line if/else branch, etc. If you don't like star > imports, then have them cause your build to fail. Problem solved! > > In this case, just because (a) Java enables a coding practice you don't > like doesn't mean (b) you have to tolerate it in your own organization. The > (a) and (b) are two separate issues with two separate debates and two > separate resolutions. > > -Archie > > On Tue, Nov 19, 2024 at 8:38?AM Pedro Lamar?o < > pedro.lamarao at prodist.com.br> wrote: > >> Em ter., 19 de nov. de 2024 ?s 11:09, Cristian Mocanu >> escreveu: >> >> >>> Knowing that code is being read many time more often than written, I >>> don't think it makes sense to optimize writing an import by hand (which no >>> one does anyway - the IDE writes it for us) to the detriment of introducing >>> confusion when reading the code outside an IDE (like a GitHub PR review). >>> >> >> Optimizing Java for GitWeb-like readers does not seem to me the way >> forward. >> GitWeb-like products are themselves moving toward "AI powered web IDE" >> status. >> >> -- >> Pedro Lamar?o >> > > > -- > Archie L. Cobbs > -------------- next part -------------- An HTML attachment was scrubbed... URL: From cvmocanu at gmail.com Tue Nov 19 15:47:56 2024 From: cvmocanu at gmail.com (Cristian Mocanu) Date: Tue, 19 Nov 2024 16:47:56 +0100 Subject: JEP-476 module import considered harmful In-Reply-To: <0BE70C77-093C-4D8E-9C21-8E9A1A90B2A6@oracle.com> References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> <34FA2CE9-1A8E-4C7C-8ED3-97BDEB4EF25A@oracle.com> <0BE70C77-093C-4D8E-9C21-8E9A1A90B2A6@oracle.com> Message-ID: HI Brian, > You seem very quick to dismiss opinions other than your own as ?invalid? and therefore not worthy of learning from. I would suggest trying to acquire the ability to look at your own (seemingly very strong) opinions with the same degree of skepticism. Otherwise, you will find a very difficult time convincing anyone to agree with you, which you will probably find frustrating. I dismiss opinions other than my own as invalid if I find them invalid, and I have an argument that backs-up my opinion of why it's invalid. I don't consider myself perfect. Every time we have a tech disagreement at work, I organize a meeting with all devs and we discuss arguments (not opinions or feelings). If someone comes with a valid argument I didn't think about, I am willing to change - and have done so in the past. And if most devs think differently from me, I will follow the team's decision and move on, even if I don't like it. Time is better spent doing something else than yelling at each other <<'tis so - no it ain't>>. > As a reminder, amber-dev is not the feature design list; if you are hoping to reopen the discussion, you have to come with arguments or data that are new. If this has been discussed already, and it was decided that introducing confusion for the sake of typing less (or some non-practical benefit such as "it's more abstract" or "it feels more compact") is acceptable, I will be quiet from now on :) Kind regards, Cristian On Tue, 19 Nov 2024 at 16:35, Brian Goetz wrote: > I think you missed the point, though. The point was not to reopen the > debate; it was to help you realize that your opinion is just one of many, > and the fact that you disagree doesn?t mean that the others are wrong (and > that we should design the language around deterring their wrongness.). > > You seem very quick to dismiss opinions other than your own as ?invalid? > and therefore not worthy of learning from. I would suggest trying to > acquire the ability to look at your own (seemingly very strong) opinions > with the same degree of skepticism. Otherwise, you will find a very > difficult time convincing anyone to agree with you, which you will probably > find frustrating. > > As a reminder, amber-dev is not the feature design list; if you are hoping > to reopen the discussion, you have to come with arguments or data that are > new. I hope that the discussion so far has convinced you that all of this > has been said before, and considered appropriately. (And ?they didn?t > agree with me? the same as ?this was not considered appropriately.?) > > > > On Nov 19, 2024, at 10:23 AM, Cristian Mocanu wrote: > > Hi Brian, > > > - I like single imports because they feel more clear > Invalid. How does knowing less feel clearer? Less code is not always > easier to understand. > > > - I like star imports because they express the dependencies more > abstractly > How is this a benefit worth having more confusing code? What is the exact > benefit? > > > - I like star imports because they make the code more compact > Totally invalid! > Junior developers don't understand that more compact code comes in 2 kinds: > * the kind that makes the code easier to understand > * the kind that makes the code harder to understand > Having to write the types when declaring variables or fields makes the > code less compact, but more readable at the same time. > > > Which is to say, opinions are all over the map > Agreed, but not all opinions are valid :) > > Kind regards, > Cristian > > > On Tue, 19 Nov 2024 at 15:22, Brian Goetz wrote: > >> To illustrate that your view about star imports is merely one of many, >> have a look at >> >> >> https://stackoverflow.com/questions/147454/why-is-using-a-wild-card-with-a-java-import-statement-bad >> >> >> Summarizing the answers to this q (considering only answers with more >> than a few upvotes): >> >> - Star imports are bad because they clutter the namespace with stuff you >> don?t need, and might conflict >> - I like single imports because they feel more clear >> - Star imports are bad because there are conflicts like java.util.List >> vs java.awt.List >> - I like star imports because they express the dependencies more >> abstractly >> - Even if there are no conflicts now, there could be conflicts arising >> later, which means star imports are less stable >> - I like star imports because they make the code more compact >> >> Which is to say, opinions are all over the map, and even among >> star-import haters, the reasons are mostly not about readability, but about >> conflict management and stability. >> >> On Nov 19, 2024, at 8:52 AM, Cristian Mocanu wrote: >> >> Hi Ron, >> >> Sorry, I incorrectly assumed that the problem is well known. >> >> You stated it almost correctly: "if a file has `import module a; import >> module b;` and it uses the type `X`, you would need to search both `a` and >> `b` to find the documentation for `X` when reading the code, rather than >> just one of them (if we had, say, import X from module a`)". >> >> To make the problem clearer - if I see class X, and I'm not sure from >> which module it comes from, I need to manually open the source code of all >> imported modules, and check if they contain the class. >> Or, much simpler, open the Java file in an IDE, and check there from what >> module it comes. >> >> You're referring to documentation, but I would like to broaden the >> problem a bit. >> To lookup documentation, I would just use my IDE, which would "know" >> which module X is coming from. >> >> The basic problem is that it's no longer obvious where X is coming from, >> only by looking at the source code. For example, if you see `StringUtils`, >> you can't tell whether it comes from Spring, or Apache Commons Lang, or >> Micrometer, or Apache Commons Codec, or Logback Logstash Encoder, or some >> other library - it can be any one of the module imports. >> >> Knowing that code is being read many time more often than written, I >> don't think it makes sense to optimize writing an import by hand (which no >> one does anyway - the IDE writes it for us) to the detriment of introducing >> confusion when reading the code outside an IDE (like a GitHub PR review). >> >> Kind regards, >> Cristian >> >> On Tue, 19 Nov 2024 at 14:39, Ron Pressler >> wrote: >> >>> Hi. >>> >>> Merely stating that you you believe some feature to be harmful is not >>> helpful feedback, because we already take it as a given that there are many >>> features that some people strongly believe are very useful while others >>> believe just as strongly to be harmful. Such a statement provides no >>> actionable information. >>> >>> What could be helpful is trying to precisely articulate a problem you?ve >>> run into. For example, if a file has `import module a; import module b;` >>> and it uses the type `X`, you would need to search both `a` and `b` to find >>> the documentation for `X` when reading the code, rather than just one of >>> them (if we had, say, import X from module a`). Is that the problem you?re >>> referring to? Why does it make reading the code so much more difficult for >>> you? >>> >>> ? Ron >>> >>> > On 19 Nov 2024, at 09:49, Cristian Mocanu wrote: >>> > >>> > Hello, >>> > >>> > My name is Cristia Mocanu, and I am a Java developer with almost 20 >>> years of experience. >>> > >>> > I was made aware of "JEP-476 module import" recently. >>> > >>> > I strongly recommend making sure this JEP is abandoned and never gets >>> merged into Java. >>> > The reason is that a module import shares the same problem with the >>> star import: it makes the code much more difficult to understand without an >>> IDE (e.g. when reviewing a PR on GitHub). >>> > The problem with the star import is so bad, that many official code >>> styles, and many teams I worked in, explicitly forbid star imports, making >>> the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport >>> rule). >>> > >>> > The module import, just like the star import, will have the very bad >>> effect of encouraging people to write code that is difficult to understand. >>> > The advantage would be that the VIM guy can type less when writing a >>> Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), >>> but the last time I wrote an import manually was probably 15 years ago - in >>> the real world, we type the class name, and IntelliJ or some other IDE >>> writes the import for us. >>> > >>> > With fewer words: this JEP has nasty disadvantages, without providing >>> any real world benefit. >>> > >>> > Kind regards, >>> > Cristian >>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Nov 19 16:28:37 2024 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 19 Nov 2024 16:28:37 +0000 Subject: [External] : Re: JEP-476 module import considered harmful In-Reply-To: References: <5614B318-CF01-4E3F-93B8-2188D779FEB2@oracle.com> Message-ID: <2D4CC886-5758-4069-9D19-D6CF0D99BDD2@oracle.com> This is, indeed, a well-known opinion. But the opposite opinion, which is also well-known, can be argued just as well: X can only come from either module `a` or module `b` but not both, and if you don?t already know X and want to understand the code, you?ll spend much more time learning X?s documentation ? with the help of an IDE or without ? than finding it; this effort will need to be repeated by anyone reading the code who doesn?t know what X does. You may counter that the extra time spent finding the documentation is not worth it, to which the other side will respond that a great many projects are not reviewed by strangers who are unfamiliar with the types used, and so there?s no point adding noise for the sake of reviewers who don?t exist. This debate can go on and on, with well-reasoned arguments on both sides. If there is one universal in programming, it is that experienced developers often disagree on what?s right, each finding their own arguments more convincing than others'. Sometimes the language designers find one side?s arguments decisively more convincing (at least in the context of Java language features), but this is not one of those times, and so, those who dislike module or star imports are welcome to not them. If, after having used the feature, you find a problem that you believe has not been recognised by the designers (that you assumed the problem you pointed out is well-known suggests that you do not believe that to be the case here) then that would be a valuable report. Otherwise, rehashing the arguments in favour of either side of an old controversy is not helpful, as it doesn?t offer any new information. ? Ron > On 19 Nov 2024, at 13:52, Cristian Mocanu wrote: > > Hi Ron, > > Sorry, I incorrectly assumed that the problem is well known. > > You stated it almost correctly: "if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`)". > > To make the problem clearer - if I see class X, and I'm not sure from which module it comes from, I need to manually open the source code of all imported modules, and check if they contain the class. > Or, much simpler, open the Java file in an IDE, and check there from what module it comes. > > You're referring to documentation, but I would like to broaden the problem a bit. > To lookup documentation, I would just use my IDE, which would "know" which module X is coming from. > > The basic problem is that it's no longer obvious where X is coming from, only by looking at the source code. For example, if you see `StringUtils`, you can't tell whether it comes from Spring, or Apache Commons Lang, or Micrometer, or Apache Commons Codec, or Logback Logstash Encoder, or some other library - it can be any one of the module imports. > > Knowing that code is being read many time more often than written, I don't think it makes sense to optimize writing an import by hand (which no one does anyway - the IDE writes it for us) to the detriment of introducing confusion when reading the code outside an IDE (like a GitHub PR review). > > Kind regards, > Cristian > > On Tue, 19 Nov 2024 at 14:39, Ron Pressler wrote: > Hi. > > Merely stating that you you believe some feature to be harmful is not helpful feedback, because we already take it as a given that there are many features that some people strongly believe are very useful while others believe just as strongly to be harmful. Such a statement provides no actionable information. > > What could be helpful is trying to precisely articulate a problem you?ve run into. For example, if a file has `import module a; import module b;` and it uses the type `X`, you would need to search both `a` and `b` to find the documentation for `X` when reading the code, rather than just one of them (if we had, say, import X from module a`). Is that the problem you?re referring to? Why does it make reading the code so much more difficult for you? > > ? Ron > > > On 19 Nov 2024, at 09:49, Cristian Mocanu wrote: > > > > Hello, > > > > My name is Cristia Mocanu, and I am a Java developer with almost 20 years of experience. > > > > I was made aware of "JEP-476 module import" recently. > > > > I strongly recommend making sure this JEP is abandoned and never gets merged into Java. > > The reason is that a module import shares the same problem with the star import: it makes the code much more difficult to understand without an IDE (e.g. when reviewing a PR on GitHub). > > The problem with the star import is so bad, that many official code styles, and many teams I worked in, explicitly forbid star imports, making the build fail if one is found (i.e. by using Checkstyle's AvoidStarImport rule). > > > > The module import, just like the star import, will have the very bad effect of encouraging people to write code that is difficult to understand. > > The advantage would be that the VIM guy can type less when writing a Java file. Don't get me wrong, I use VIM myself (even the IdeaVim plugin), but the last time I wrote an import manually was probably 15 years ago - in the real world, we type the class name, and IntelliJ or some other IDE writes the import for us. > > > > With fewer words: this JEP has nasty disadvantages, without providing any real world benefit. > > > > Kind regards, > > Cristian > From kfogel at dawsoncollege.qc.ca Tue Nov 19 16:49:36 2024 From: kfogel at dawsoncollege.qc.ca (Kenneth Fogel) Date: Tue, 19 Nov 2024 16:49:36 +0000 Subject: Ode to Implicitly Declared Classes & Instance Main Methods plus Module Import Declarations Message-ID: For me, as a retired educator, Implicitly Declared Classes and Instance Main Methods along with Module Import Declarations simplifies teaching and learning Java. As someone who has taught Java to beginners for almost 25 years (BASIC, Pascal, C, & C++ previously) I can say without hesitation that Java has been hard to learn. My opinions on teaching Java to students at any age, turns out I?m old, has changed significantly over the past few years. When I started teaching my focus in the classroom was to train my students to thoroughly understand the syntax of the language and then apply that syntax to problem solving. I have come to the realization that this was likely the wrong approach, far too late in my career. What is important is problem solving and the organization of the solution. Syntax is secondary. In my Intro to Java course I found that the decorations of Java OOP programming got in the way of problem solving. If I am working on the very simple problem of writing a program that can make change (x pennies, y quarters, z dollars, etc), I do not want to also tell my students to include the decorations. Just solve the problem and fall in love with the mod operator. From my limited perspective the evilest expression in Java is System.out.println (confusingly this does not require an import). This is not to say that the proper construction of classes and employing access control will be ignored but it should not be a large part of the first few classes in an intro course. The capability of Java is, in my opinion, unparalleled due to the bureaucracy (in this case a good thing) that ensures that every JSR and JEP enhances the language. The JEPs (I am confused by the numbering so I used the titles in my first sentence above)) have been well thought out (well maybe it could use a readNum ?). I look forward to starting my upcoming online Intro to Java course, I am allowed to teach one course a year at Concordia University even though I am retired, where I can teach problem solving and basic data structures with minimal overhead, hence my love of module java.base implicit import. By the end of my 30-hour online course my students will have learned how to create classes and understand what public, private, and protected mean and the role of static elements, just not on day one anymore. One last point, with these JEPs plus Launch Multi-File Source-Code Programs I believe I can also start teaching with just a text editor and a terminal/console and leave the IDE for later in the course. Ken -------------- next part -------------- An HTML attachment was scrubbed... URL: From rleach at rleach.id.au Wed Nov 20 04:11:53 2024 From: rleach at rleach.id.au (Ryan Leach) Date: Wed, 20 Nov 2024 14:41:53 +1030 Subject: Jep 468: What about 'new' syntax? Message-ID: I've recently become aware of JEP 468, due to a post on Reddit showing off its capability, after hearing about it in recent Java talks. I love it, however, I think the current syntax for invoking a with {} block inhibits readability by people, especially students of Java. At the moment in the Java language, most instances of instance creation (that I'm aware of) is done using the new keyword, or factory methods that call it on your behalf. I'd instead propose that derived record creation uses the keyword `new` **somewhere** in the syntax, to keep with consistency. e.g. borrowing some inspiration from java's anonymous classes, the following syntax might work. ``` Point finalLoc = new Point with nextLoc { x *= 2; y *= 2; z *= 2; }; ``` it feels a little wordy, so I'm not sure, but it might open the door to the following future changes too, if C# style object initialization was added, ``` Point finalLoc = new Point with { x = 2; y = 2; z = 2; }; ``` The thing I fear with this, is it starts looking very similar to anonymous inner classes, if it wasn't for the with keyword and it being applied to records, so maybe there's a good reason that I'm missing that derived record creation looks succinctly different. Ideally new class creation would look distinctly different from new instance creation, but alas, it's impossible to change the past syntax. Just as food for thought, imagining using both anonymous inner classes and with's on classes, ``` Point finalLoc = new Point() { // class changes } with nextLoc { // derived class edits }; ``` But this would likely be bad code anyway, due to mixed types being used with 'with' blocks, and an instance being thrown away just to create the derived class, but it's interesting to consider the syntax would be flexible enough to work like this, at least in an abstract ideation sense. I recognize that the JEP labelled `Analogy to pattern matching`, it shows how the proposed syntax looks similar to a common switch expression idiom that people have adopted for records, which is good and all, but the switch expression uses the new keyword, making it stand out, and I wonder how widely adopted it is vs 'new'. I don't want to stall the feature, but I believe that using the current syntax will impact new users in learning Java, ever so slightly. Forgive me if I am out of pocket, I realize this looks a lot like classic bike-shedding but I'm relatively junior in the Java sphere, and I've seen people almost begging for newer users to provide feedback on JEPs in reddit comments instead of talking on social media. -- Ryan Leach -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Wed Nov 20 06:56:20 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Wed, 20 Nov 2024 08:56:20 +0200 Subject: Jep 468: What about 'new' syntax? In-Reply-To: References: Message-ID: Hello. Just a side note: AFAIK "new", by spec, guarantees that new instance of object is returned, so it can potentially affect implementation requirements for compilers, forcing them to copy records even if with expression block is empty, i.e. var b = new a with {} Unless there is a exception for such syntax, new will mandate that b != a (by reference), which would oblige doing basically useless extra work ( Since "with" performs shallow copy and records are immutable, so a and b will, by any "external observer", considered the same except for identity, if Im not missing something ) On Wed, Nov 20, 2024, 06:12 Ryan Leach wrote: > I've recently become aware of JEP 468, due to a post on Reddit showing off > its capability, after hearing about it in recent Java talks. > > I love it, however, I think the current syntax for invoking a with {} > block inhibits readability by people, especially students of Java. > > At the moment in the Java language, most instances of instance creation > (that I'm aware of) is done using the new keyword, or factory methods that > call it on your behalf. > > I'd instead propose that derived record creation uses the keyword `new` > **somewhere** in the syntax, to keep with consistency. > > e.g. borrowing some inspiration from java's anonymous classes, the > following syntax might work. > > ``` > Point finalLoc = new Point with nextLoc { > x *= 2; > y *= 2; > z *= 2; > }; > ``` > > it feels a little wordy, so I'm not sure, but it might open the door to > the following future changes too, if C# style object initialization was > added, > > ``` > Point finalLoc = new Point with { > x = 2; > y = 2; > z = 2; > }; > ``` > > The thing I fear with this, is it starts looking very similar to anonymous > inner classes, if it wasn't for the with keyword and it being applied to > records, so maybe there's a good reason that I'm missing that derived > record creation looks succinctly different. > Ideally new class creation would look distinctly different from new > instance creation, but alas, it's impossible to change the past syntax. > > Just as food for thought, imagining using both anonymous inner classes and > with's on classes, > > ``` > Point finalLoc = new Point() { > // class changes > } with nextLoc { > // derived class edits > }; > ``` > > But this would likely be bad code anyway, due to mixed types being used > with 'with' blocks, and an instance being thrown away just to create the > derived class, but it's interesting to consider the syntax would be > flexible enough to work like this, at least in an abstract ideation sense. > > > I recognize that the JEP labelled `Analogy to pattern matching`, it shows > how the proposed syntax looks similar to a common switch expression idiom > that people have adopted for records, which is good and all, but the switch > expression uses the new keyword, making it stand out, and I wonder how > widely adopted it is vs 'new'. > > I don't want to stall the feature, but I believe that using the current > syntax will impact new users in learning Java, ever so slightly. > > Forgive me if I am out of pocket, I realize this looks a lot like classic > bike-shedding but I'm relatively junior in the Java sphere, and I've seen > people almost begging for newer users to provide feedback on JEPs in reddit > comments instead of talking on social media. > > -- > Ryan Leach > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Nov 20 13:18:16 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 20 Nov 2024 08:18:16 -0500 Subject: Jep 468: What about 'new' syntax? In-Reply-To: References: Message-ID: <7ff18a40-fe13-41d9-81f9-1b911060bc68@oracle.com> > Hello. Just a side note: AFAIK "new", by spec, guarantees that new > instance of object is returned > Historically this is true.? However, when Valhalla gives us value types, the "new"-ness of a new value object will be indistinguishable from an old object.? So I would advise against taking this "new means new" dictum too literally. > , so it can potentially affect implementation requirements for > compilers, forcing them to copy records even if with expression block > is empty, i.e. > > var b = new a with {} > > Unless there is a exception for such syntax, new will mandate that b > != a (by reference) > ... unless a and b are value objects, in which case == compares their state, not their identity (because they have none.) From duke at openjdk.org Wed Nov 20 14:17:21 2024 From: duke at openjdk.org (duke) Date: Wed, 20 Nov 2024 14:17:21 GMT Subject: git: openjdk/amber-docs: Added the JDK24 JEPs Message-ID: <8b25ba8d-4e5b-468a-a54e-990f1ea32936@openjdk.org> Changeset: 266a6f2a Branch: master Author: Gavin Bierman Date: 2024-11-20 14:12:24 +0000 URL: https://git.openjdk.org/amber-docs/commit/266a6f2a264bacb471cfffddbed02c1bc47fef59 Added the JDK24 JEPs ! site/_index.md From rotanolexandr842 at gmail.com Wed Nov 20 16:46:35 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Wed, 20 Nov 2024 18:46:35 +0200 Subject: Jep 468: What about 'new' syntax? In-Reply-To: <7ff18a40-fe13-41d9-81f9-1b911060bc68@oracle.com> References: <7ff18a40-fe13-41d9-81f9-1b911060bc68@oracle.com> Message-ID: Yea, Brian, I agree with you. I wasn't even proposing that as argument against proposal, more like a nasty little detail, hence this is a "side note". Moreover, if (or, I hope, when) more record features expanded to all classes, compiler will still be required to clone non-value objects since they can be mutable, and with implies making a copy (although not always actually has to do it as in my example). But I gave more thought to this today, and what I have concluded is that I would rather vote against it. The reason is that only value that "new" brings to the table here is better "human readability". But, for me it seems more like a mumbling. I am, I guess, more of a writablity guy than most people here, so I don't really like mumbling. Also it is some noise for reader. "New" here does not help resolve some ambiguity nor for compiler nor for reader, at least as it seems for me. That's why I would say I am not a fan of this syntax On Wed, Nov 20, 2024, 15:18 Brian Goetz wrote: > > > Hello. Just a side note: AFAIK "new", by spec, guarantees that new > > instance of object is returned > > > > Historically this is true. However, when Valhalla gives us value types, > the "new"-ness of a new value object will be indistinguishable from an > old object. So I would advise against taking this "new means new" > dictum too literally. > > > , so it can potentially affect implementation requirements for > > compilers, forcing them to copy records even if with expression block > > is empty, i.e. > > > > var b = new a with {} > > > > Unless there is a exception for such syntax, new will mandate that b > > != a (by reference) > > > > ... unless a and b are value objects, in which case == compares their > state, not their identity (because they have none.) > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Nov 20 16:52:57 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 20 Nov 2024 11:52:57 -0500 Subject: Jep 468: What about 'new' syntax? In-Reply-To: References: <7ff18a40-fe13-41d9-81f9-1b911060bc68@oracle.com> Message-ID: <0cd892ff-4387-4e69-b08e-0f84f12f8f86@oracle.com> While I would not say that the `x with B` syntax is written in stone, I don't think just stapling `new` to it is helpful.? I will point out (as I have several times before) that I think trying to coerce the `with` syntax into a way to get by-name invocation of constructors is a very bad move.? I totally get why people keep reaching for it, but it's the wrong way to get there.? Maybe we'll get there some other way, maybe we won't, but we definitely won't get there this way. On 11/20/2024 11:46 AM, Olexandr Rotan wrote: > > Yea, Brian, I agree with you. I wasn't even proposing that as argument > against proposal, more like a nasty little detail, hence this is a > "side note". Moreover, if (or, I hope, when) more record features > expanded to all classes, compiler will still be required to clone > non-value objects since they can be mutable, and with implies making a > copy (although not always actually has to do it as in my example). > > But I gave more thought to this today, and what I have concluded is > that I would rather vote against it. The reason is that only value > that "new" brings to the table here is better "human readability". > But, for me it seems more like a mumbling. I am, I guess, more of a > writablity guy than most people here, so I don't really like mumbling. > Also it is some noise for reader. "New" here does not help resolve > some ambiguity nor for compiler nor for reader, at least as it seems > for me. That's why I would say I am not a fan of this syntax > > > On Wed, Nov 20, 2024, 15:18 Brian Goetz wrote: > > > > Hello. Just a side note: AFAIK "new", by spec, guarantees that new > > instance of object is returned > > > > Historically this is true.? However, when Valhalla gives us value > types, > the "new"-ness of a new value object will be indistinguishable > from an > old object.? So I would advise against taking this "new means new" > dictum too literally. > > > , so it can potentially affect implementation requirements for > > compilers, forcing them to copy records even if with expression > block > > is empty, i.e. > > > > var b = new a with {} > > > > Unless there is a exception for such syntax, new will mandate > that b > > != a (by reference) > > > > ... unless a and b are value objects, in which case == compares their > state, not their identity (because they have none.) > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Nov 21 18:45:03 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 21 Nov 2024 13:45:03 -0500 Subject: JEP 495 Question In-Reply-To: References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> Message-ID: <9a853a3c-69df-407c-abc7-5550367c0536@oracle.com> I'd like to validate this further, but I'd like to seek feedback _from educators only_.? (Please include in your answer how long you've been teaching Java *formally*.) > I am with Stephen here. > > For effective learning, the gating factor is complexity, not > verbosity. The regularity of IO.println wins over the implicit import. I would like to assess how broadly this opinion is held _by educators_.? The three positions that have been espoused by various proponents are: ?- println is simpler for learners, and so the speed bump of going from there to IO.println when going to non-simple compilation units is worth it. ?- IO.println is equally simple for learners, and has the benefit of uniformity, no need for static import. ?- IO.println is actually *simpler* for learners, because the IO provides context to what comes after the dot. It is easily imaginable for experienced developers to hold any of these views; consider all of those as having been read into the record.? I want to hear _from educators_ about which they would be more comfortable teaching, and why. Thanks in advance for keeping this channel clear for the experienced educators to speak. From kfogel at dawsoncollege.qc.ca Thu Nov 21 20:59:27 2024 From: kfogel at dawsoncollege.qc.ca (Kenneth Fogel) Date: Thu, 21 Nov 2024 20:59:27 +0000 Subject: JEP 495 Question In-Reply-To: <9a853a3c-69df-407c-abc7-5550367c0536@oracle.com> References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> <9a853a3c-69df-407c-abc7-5550367c0536@oracle.com> Message-ID: You all probably know my position on this matter. ?- println is simpler for learners, and so the speed bump of going from there to IO.println when going to non-simple compilation units is worth it. I believe that dot notation has never been simple to explain or describe at the beginning stages of learning Java. The need to gloss over what a class, an object, and what static means in the first sessions of a course has always troubled me. I have described System.out.println as evil but only in the context of a new learner. Today's students can plug into what it means to write software in ways that most of us could not. What they see is this language called Python that lets a learner evolve from spaghetti code (i.e. BASIC) to structured code (i.e. Pascal) and finally to OOP (not quite but kind of C++). I taught Java for most of my career in an OOP first manner. I felt, and still do, that Separation of Concerns is the most important architectural concept. My mistake was to believe that the only way to adopt this principle successfully was in an OOP only environment. I believe that Python has shown us, or maybe just me, that OOP should not be the only way to teach Java. I feel that problem solving is what is most important. Many of what we call decorations interferes with this. I love println and especially readln. Please do not make me have to use the atrocious dot in my first sessions of my intro course. As always, feel free to use my name in vain, Ken Fogel Computer Science Instructor for 31 years, now retired for 3 years. From mattrpav at gmail.com Fri Nov 22 00:14:48 2024 From: mattrpav at gmail.com (Matt Pavlovich) Date: Thu, 21 Nov 2024 18:14:48 -0600 Subject: JEP 495 Question In-Reply-To: <9a853a3c-69df-407c-abc7-5550367c0536@oracle.com> References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> <9a853a3c-69df-407c-abc7-5550367c0536@oracle.com> Message-ID: <4CAD4179-48AC-4185-9FD2-26C8D4D6BB1D@gmail.com> Hello- How about a series of classes that provide the same I/O methods (and behavior!) across the spectrum of I/O types? This would allow: 1. New coders to learn on the console, which provides the benefit of immediate feedback loop 2. Allow new coders to transition from Console to File to Socket by changing only a single word in the source code Console.readInt Console.readIntln Console.writeInt Console.writeIntln Socket.readInt Socket.writeInt .. File.readInt File.writeInt ,,, Properties.readInt Properties.writeInt ,,, Thanks, Matt Pavlovich Instructor: 1,000+ hours of classroom Teacher: 8th grade robotics/engineering/programming > On Nov 21, 2024, at 12:45?PM, Brian Goetz wrote: > > I'd like to validate this further, but I'd like to seek feedback _from educators only_. (Please include in your answer how long you've been teaching Java *formally*.) > >> I am with Stephen here. >> >> For effective learning, the gating factor is complexity, not verbosity. The regularity of IO.println wins over the implicit import. > > I would like to assess how broadly this opinion is held _by educators_. The three positions that have been espoused by various proponents are: > > - println is simpler for learners, and so the speed bump of going from there to IO.println when going to non-simple compilation units is worth it. > - IO.println is equally simple for learners, and has the benefit of uniformity, no need for static import. > - IO.println is actually *simpler* for learners, because the IO provides context to what comes after the dot. > > It is easily imaginable for experienced developers to hold any of these views; consider all of those as having been read into the record. I want to hear _from educators_ about which they would be more comfortable teaching, and why. > > Thanks in advance for keeping this channel clear for the experienced educators to speak. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Nov 22 00:19:37 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 21 Nov 2024 19:19:37 -0500 Subject: JEP 495 Question In-Reply-To: <4CAD4179-48AC-4185-9FD2-26C8D4D6BB1D@gmail.com> References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> <9a853a3c-69df-407c-abc7-5550367c0536@oracle.com> <4CAD4179-48AC-4185-9FD2-26C8D4D6BB1D@gmail.com> Message-ID: <565c8ba8-11ba-4edf-b105-14e51e9a3176@oracle.com> You pass the "only respond if you are an instructor" test, but I was asking a very specific question: what can we say about the relative learnability of `IO.println` vs `println`? There's infinite room to design new APIs but we're looking for feedback on a specific question. On 11/21/2024 7:14 PM, Matt Pavlovich wrote: > Hello- > > How about a series of classes that provide the same I/O methods (and > behavior!) across the spectrum of I/O types? > > This would allow: > 1. New coders to learn on the console, which provides the benefit of > immediate feedback loop > 2. Allow new coders to transition from Console to File to Socket by > changing only a single word in the source code > > Console.readInt > Console.readIntln > Console.writeInt > Console.writeIntln > > Socket.readInt > Socket.writeInt > .. > > File.readInt > File.writeInt > ,,, > > Properties.readInt > Properties.writeInt > ,,, > > > Thanks, > Matt Pavlovich > > Instructor: 1,000+ hours of classroom > Teacher: 8th grade robotics/engineering/programming > >> On Nov 21, 2024, at 12:45?PM, Brian Goetz wrote: >> >> I'd like to validate this further, but I'd like to seek feedback >> _from educators only_.? (Please include in your answer how long >> you've been teaching Java *formally*.) >> >>> I am with Stephen here. >>> >>> For effective learning, the gating factor is complexity, not >>> verbosity. The regularity of IO.println wins over the implicit import. >> >> I would like to assess how broadly this opinion is held _by >> educators_.? The three positions that have been espoused by various >> proponents are: >> >> ?- println is simpler for learners, and so the speed bump of going >> from there to IO.println when going to non-simple compilation units >> is worth it. >> ?- IO.println is equally simple for learners, and has the benefit of >> uniformity, no need for static import. >> ?- IO.println is actually *simpler* for learners, because the IO >> provides context to what comes after the dot. >> >> It is easily imaginable for experienced developers to hold any of >> these views; consider all of those as having been read into the >> record.? I want to hear _from educators_ about which they would be >> more comfortable teaching, and why. >> >> Thanks in advance for keeping this channel clear for the experienced >> educators to speak. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From mattrpav at gmail.com Fri Nov 22 01:02:02 2024 From: mattrpav at gmail.com (Matt Pavlovich) Date: Thu, 21 Nov 2024 19:02:02 -0600 Subject: JEP 495 Question In-Reply-To: <565c8ba8-11ba-4edf-b105-14e51e9a3176@oracle.com> References: <10a97843-2379-4bc6-921e-787bc0d70149@oracle.com> <9a853a3c-69df-407c-abc7-5550367c0536@oracle.com> <4CAD4179-48AC-4185-9FD2-26C8D4D6BB1D@gmail.com> <565c8ba8-11ba-4edf-b105-14e51e9a3176@oracle.com> Message-ID: <568EEDE3-EE6E-48E5-A2C1-D4A2DCBB20EF@gmail.com> The dots are a real thing. New coders are often also new to computers. One thing that is different about middle school age learners is that they have grown up with computers (phones and tablets) that work correctly (for the most part) *all* the time. They don?t have to compile a kernel, install a bunch of packages, fix defaults for their use case, or dive into a config.sys or autoexec.bat or a reg edit ? let alone even peak at a file system. I was surprised by how many kids don?t really know how images are stored on a drive? they?ve only known thumbnails, not filenames. Most things are a black box of ?it just works?. The first introduction of ?dots? is in filenames. There is a heavy context switch to figure ?ok .. this thing we are doing with a ?dot' is not about a file .. it is some sort of way to have the computer do a thing? That being said, I don?t think the import static is that big of a deal. As a student builds up from console to file to socket, the imports become a conversation about learning to troubleshoot. Things start to click once they try to use a class or method that is imported in the example source file in the instruction book, but not their source file. Students learn to eye scan the "import area of the file? and start to remember that they need to add that ?magic line" and then it compiles and runs without changing any other ?code?. Python has the same thing? you can't do hashes or networking without the proper headers (and corresponding libraries installed). Cautiously peeking back at my previous suggestion ;-) ? I think having the step-up be *same* in terms of words and characters on the screen is super valuable in getting the lightbulb to go off that reading and writing to things with a computer is all the same? console, file, network, properties file, etc. It?s how computers work and the next generation of coder has never had to learn *how* computers work. Having different method names and handles adds a divergence that doesn?t connect back to the reality that it?s all the same under the covers. Thanks, Matt How?s this for a Mr Pav?s Java 101? Lesson 1: import static Console.*; void main() { String name = readln("Please enter your name: "); write("Pleased to meet you, "); writeln(name); } Lesson 2: (Introduce types other than text) import static Console.*; void main() { int age = readInt("Please enter your age: "); write(?Your age is, ?); writeInt(age); } Lesson 3: (qualify read/write methods) import java.io.Console; void main() { int age = Console.readInt("Please enter your age: "); Console.write(?Your age is, ?); Console.writeIntln(age); } Lesson 4: (add a file) import java.io.Console; import java.io.File; void main() { int age = Console.readInt("Please enter your age: "); Console.write(?Writing age to file, ?); Console.writeIntln(age); File file = new File(?age.txt?); file.writeIntln(age); // now go inspect age.txt } Lesson 5: (replace file with a network socket) import java.io.Console; import java.net.Socket; void main() { int age = Console.readInt("Please enter your age: "); Console.write(?Sending age to server, ?); Console.writeIntln(age); Socket socket = new Socket(?127.0.01?, 4321); socket.writeIntln(age); // now go inspect server-side } > On Nov 21, 2024, at 6:19?PM, Brian Goetz wrote: > > You pass the "only respond if you are an instructor" test, but I was asking a very specific question: what can we say about the relative learnability of `IO.println` vs `println`? > > There's infinite room to design new APIs but we're looking for feedback on a specific question. > > > On 11/21/2024 7:14 PM, Matt Pavlovich wrote: >> Hello- >> >> How about a series of classes that provide the same I/O methods (and behavior!) across the spectrum of I/O types? >> >> This would allow: >> 1. New coders to learn on the console, which provides the benefit of immediate feedback loop >> 2. Allow new coders to transition from Console to File to Socket by changing only a single word in the source code >> >> Console.readInt >> Console.readIntln >> Console.writeInt >> Console.writeIntln >> >> Socket.readInt >> Socket.writeInt >> .. >> >> File.readInt >> File.writeInt >> ,,, >> >> Properties.readInt >> Properties.writeInt >> ,,, >> >> >> Thanks, >> Matt Pavlovich >> >> Instructor: 1,000+ hours of classroom >> Teacher: 8th grade robotics/engineering/programming >> >>> On Nov 21, 2024, at 12:45?PM, Brian Goetz wrote: >>> >>> I'd like to validate this further, but I'd like to seek feedback _from educators only_. (Please include in your answer how long you've been teaching Java *formally*.) >>> >>>> I am with Stephen here. >>>> >>>> For effective learning, the gating factor is complexity, not verbosity. The regularity of IO.println wins over the implicit import. >>> >>> I would like to assess how broadly this opinion is held _by educators_. The three positions that have been espoused by various proponents are: >>> >>> - println is simpler for learners, and so the speed bump of going from there to IO.println when going to non-simple compilation units is worth it. >>> - IO.println is equally simple for learners, and has the benefit of uniformity, no need for static import. >>> - IO.println is actually *simpler* for learners, because the IO provides context to what comes after the dot. >>> >>> It is easily imaginable for experienced developers to hold any of these views; consider all of those as having been read into the record. I want to hear _from educators_ about which they would be more comfortable teaching, and why. >>> >>> Thanks in advance for keeping this channel clear for the experienced educators to speak. >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From rleach at rleach.id.au Fri Nov 22 11:46:19 2024 From: rleach at rleach.id.au (Ryan Leach) Date: Fri, 22 Nov 2024 22:16:19 +1030 Subject: Jep 468: What about 'new' syntax? In-Reply-To: <1a8fddf6-b5a7-4bce-ac2d-883cf8f6b6cb@oracle.com> References: <7ff18a40-fe13-41d9-81f9-1b911060bc68@oracle.com> <0cd892ff-4387-4e69-b08e-0f84f12f8f86@oracle.com> <1a8fddf6-b5a7-4bce-ac2d-883cf8f6b6cb@oracle.com> Message-ID: Since first responding I've now done considerable brushing up on the various amber archives, and have gained a better understanding of the resistance to anything that looks like by-name abuse of derived record creation. I look forward to being able to use 'with' with other patterns, and agree, that, given the transformation that Java will undertake in the coming years with the various plans for pattern matching, that this will probably fit in fine as "Java-like". Would still prefer to have 'new', but alas, I'm just one small opinion amongst many, and don't have anything further to add discussion wise that Remi hasn't covered in the expert group, and would not have replied if I hadn't managed to accidentally drop amber-dev as a cc. On Fri, Nov 22, 2024 at 10:47?AM Brian Goetz wrote: > Let's set aside the syntax; suffice it to say there are probably several > million who like and several million who don't, for basically any option, > so one more vote yea/nay does not move the needle. > > But I do want to take exception to "not powerful enough" (and also, to > some degree, "not Java-like"); it is actually far more powerful than what > other languages do in this space, but not in an obvious way. > > Most languages that have a feature like this are fairly constrained. > Either you can only do property assignments, or there is some "DSL" subset > of the language that you can use. The block on the RHS, on the other hand, > can contain any Java statement -- loops, conditionals, whatever. It is not > a constrained subset. > > You can consider a `with` block `x with { STATEMENTS }` to be largely > equivalent to: > > x = switch (x) { > case TypeOfX(COMPONENTS) -> { STATEMENTS; yield new > TypeOfX(COMPONENTS); } > } > > The `with` syntax is largely sugar for this. > > When you can declare patterns in arbitrary classes, `with` will work with > them as well. > > > > > > On 11/21/2024 1:25 AM, Ryan Leach wrote: > > That's fair Brian. > > In truth when I was writing this post, the stuff about object initializers > was tacked on as I was thinking through potential pitfalls and potential > benefits of alternative syntax then that proposed by the JEP. > > I still think my original point has strength in 'new' being used for > readability and teachability. > > If it seems wordy, I agree, but I'm not in love with the 'with' keyword, > reviewing other languages, it seems there's a vast variety of uses for the > 'with' keyword already existing, and independently, various ways of > tackling the copy-with-changes problem. > > The ambiguous use of 'with' does mean it's kinda free real estate, > especially since some uses coincide with record / struct copying. > > The only benefit I can see from the current preview, where `with` can only > be used with records (which seems a little restrictive) is the ability to > treat them like left hand side values, and thus able to use stuff like ` =* > 2` which seems a little niche, but I think, innovative. > > But it just doesn't feel "Java-like" and doesn't provide enough power to > justify its own keyword in its current form (in my opinion). > > (If there's loose ideation level plans for future directions to take it, > that's probably why.) > > A review of how other languages do record/struct/object copying looks like > it's a choice of either > > 1. object destructuring syntax. > 2. 'with'-like copying (even if it uses other symbols or keywords) > 3. Copy methods with by-name optional parameters. > > So I'm kinda at a loss on whether this is even the best way to approach > this solution now after looking into it more. > > --- > > Where can I find reading on how this feature was developed? I feel like > I'm probably retreading old ground, perhaps needlessly. > > --- > > As a brief aside for context, I've had professional work as a C# > programmer in the past, and I've experienced friction to the amount of > 'needless' language additions they've made vs languages like Scala, > especially in regards to stuff like all the different property syntax that > exists, because of the amount of "fiddling" required as a class evolves as > the syntax doesn't always lead itself well to being edited between one form > and another. > > For this reason, I'm a little against changes that don't feel orthogonal > or language-native being bolted on to fix specific problems. > > On Thu, 21 Nov 2024, 3:23?am Brian Goetz, wrote: > >> While I would not say that the `x with B` syntax is written in stone, I >> don't think just stapling `new` to it is helpful. I will point out (as I >> have several times before) that I think trying to coerce the `with` syntax >> into a way to get by-name invocation of constructors is a very bad move. I >> totally get why people keep reaching for it, but it's the wrong way to get >> there. Maybe we'll get there some other way, maybe we won't, but we >> definitely won't get there this way. >> >> >> On 11/20/2024 11:46 AM, Olexandr Rotan wrote: >> >> Yea, Brian, I agree with you. I wasn't even proposing that as argument >> against proposal, more like a nasty little detail, hence this is a "side >> note". Moreover, if (or, I hope, when) more record features expanded to all >> classes, compiler will still be required to clone non-value objects since >> they can be mutable, and with implies making a copy (although not always >> actually has to do it as in my example). >> >> But I gave more thought to this today, and what I have concluded is that >> I would rather vote against it. The reason is that only value that "new" >> brings to the table here is better "human readability". But, for me it >> seems more like a mumbling. I am, I guess, more of a writablity guy than >> most people here, so I don't really like mumbling. Also it is some noise >> for reader. "New" here does not help resolve some ambiguity nor for >> compiler nor for reader, at least as it seems for me. That's why I would >> say I am not a fan of this syntax >> >> On Wed, Nov 20, 2024, 15:18 Brian Goetz wrote: >> >>> >>> > Hello. Just a side note: AFAIK "new", by spec, guarantees that new >>> > instance of object is returned >>> > >>> >>> Historically this is true. However, when Valhalla gives us value types, >>> the "new"-ness of a new value object will be indistinguishable from an >>> old object. So I would advise against taking this "new means new" >>> dictum too literally. >>> >>> > , so it can potentially affect implementation requirements for >>> > compilers, forcing them to copy records even if with expression block >>> > is empty, i.e. >>> > >>> > var b = new a with {} >>> > >>> > Unless there is a exception for such syntax, new will mandate that b >>> > != a (by reference) >>> > >>> >>> ... unless a and b are value objects, in which case == compares their >>> state, not their identity (because they have none.) >>> >>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Nov 26 04:58:07 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 25 Nov 2024 23:58:07 -0500 Subject: My Experience with Primitive Patterns Message-ID: Hello Amber Dev, I just wanted to post my experience with using Primitive Patterns when trying it out in a non-trivial project. First off, one of the things that stood out to me immediately is how little effort it became to isolate and properly handle edge cases. Previously, if I wanted to provide a validation for all the different ways that this int could be wrong, I would need a separate if statement or a library. Typically, I would just group all of my checks into a single if check or boolean, leading to error prone-code. But now, just a simple when clause on a switch. Then, just throw on the right hand side for the bad cases, and return itself on the good cases. My validation code was easier to work with. Another thing that I DEEPLY appreciated was the exhaustiveness checking for boolean. That made a lot of my code a lot more explicit, which I appreciated. Granted, it ended up being more verbose, but I don't mind verbosity. One minor annoyance, this one error message was a bit difficult to follow without looking things up. error: duplicate unconditional pattern This error threw me for a loop when I first saw it. I NOW know it is exactly what it means (after having done the necessary research from the amber docs), but the error message could do with a bit more context. Maybe clarify which case is the duplicate? If that's a hard thing to do, then no worries. I only raise this issue in case the assumption is that the average Java programmer will look at that error and have a good idea of what it means without doing a non-trivial amount of reading. Honestly, there's not much else to write. It all just worked. I will give Primitive Patterns another test in the future, once I have time. Thank you all for your time and attention. David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 26 14:21:28 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 26 Nov 2024 09:21:28 -0500 Subject: My Experience with Primitive Patterns In-Reply-To: References: Message-ID: <6a1abaa7-8231-4bc6-8571-72c1ea31d792@oracle.com> Thanks David.? People don't always post their "I tried out the new thing and it worked as advertised" experiences. One thing that is often misunderstood about this JEP is that it is more about _regularization_ than adding a whizzy cool new feature (which often causes people to respond with "but I don't see when I would use it" comments.)? The point of "regularization" activities like this is that sharp edges that we've just gotten used to, can be handled within the normal framework.? When it works right, you shouldn't notice it at all. (Regarding switching over booleans: a useful thought experiment would be, if we had exhaustive switch expressions over booleans in 1995, would we even have wanted to add a ternary?? (It's a thought experiment, doesn't need to become a discussion here.)) > error: duplicate unconditional pattern > > This error threw me for a loop when I first saw it. error: neither throwing nor looping in your example Seriously, it is reasonable for the error message to try to identify which patterns are unconditional.? We do this with overload selection conflicts, with type inference conflicts, and it seems reasonable to do so with pattern conflicts as well. On 11/25/2024 11:58 PM, David Alayachew wrote: > Hello Amber Dev, > > I just wanted to post my experience with using Primitive Patterns when > trying it out in a non-trivial project. > > First off, one of the things that stood out to me immediately is how > little effort it became to isolate and properly handle edge cases. > Previously, if I wanted to provide a validation for all the different > ways that this int could be wrong, I would need a separate if > statement or a library. Typically, I would just group all of my checks > into a single if check or boolean, leading to error prone-code. But > now, just a simple when clause on a switch. Then, just throw on the > right hand side for the bad cases, and return itself on the good > cases. My validation code was easier to work with. > > Another thing that I DEEPLY appreciated was the exhaustiveness > checking for boolean. That made a lot of my code a lot more explicit, > which I appreciated. Granted, it ended up being more verbose, but I > don't mind verbosity. > > One minor annoyance, this one error message was a bit difficult to > follow without looking things up. > > error: duplicate unconditional pattern > > This error threw me for a loop when I first saw it. I NOW know it is > exactly what it means (after having done the necessary research from > the amber docs), but the error message could do with a bit more > context. Maybe clarify which case is the duplicate? If that's a hard > thing to do, then no worries. I only raise this issue in case the > assumption is that the average Java programmer will look at that error > and have a good idea of what it means without doing a non-trivial > amount of reading. > > Honestly, there's not much else to write. It all just worked. I will > give Primitive Patterns another test in the future, once I have time. > > Thank you all for your time and attention. > David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From oyvind at kvien.no Tue Nov 26 10:13:43 2024 From: oyvind at kvien.no (=?UTF-8?Q?=C3=98yvind_Kvien?=) Date: Tue, 26 Nov 2024 11:13:43 +0100 Subject: "With" for records and the default constructor Message-ID: I see there're no plans of including the default constructor with withers for record creation, and a manually created static builder has previously been suggested instead with default values in the constructor. In my opinion ommiting the default constructor to be used with withers would be a big mistake. A very common scenario in smaller services is to use simple data structures to pass data to the api of other services and message brokers. In these cases the data contains mostly primitive types and (these are not domain models). Setting default values manually in the default constructor, as suggested would be very tedious and lots of typing. Also many times a default value is not even possible since it could mean setting the object in a wrong initial state. Forcing the use of default values I think is a bad approach. It would therefore be immensely useful if the default record constructor could be used with withers. The compiler needs of course to enforce that all parameters in the default constructor are set. Below is a real world example of how building a request body to an internal service in my organization, used for getting access tokens for test purposes, would look like using withers and the default constructor. The following records describes the simple data structure for the request body to the api of the service (which gets serialized to json). record RequestBody( String audience, boolean withoutDefaultClientClaims, boolean withoutDefaultUserClaims, boolean createDPoPTokenWithDPoPProof, ExpirationParameters expirationParameters, ClientClaimsParameters clientClaimsParameters, @Nullable UserClaimsParameters userClaimsParameters, @Nullable DPoPProofParameters dPoPProofParameters) { } record ExpirationParameters( int expirationTimeInSeconds) { } record ClientClaimsParameters( List scope, String orgnrParent, String clientId, String clientName, String jti) { } record UserClaimsParameters( String pid, String hprNumber, String name, String givenName, String middleName, String familyName, String securityLevel, String assuranceLevel, String amr) { } record DPoPProofParameters( String htuClaimValue, String htmClaimValue) { } Using withers with the default constructor would look as suggested in the method below. It's very easy to visualise the json structure that the RequestBody record get serialized into when the parameter names are present. Also there's no need to use default values which is a big win, and the compiler enforces that all parameters are set. This way the record is never instantiated in a wrong state. Note that no parenthesis are used after 'new RequestBody' for the default constructor as a syntax suggestion. public static RequestBody createRequestBodyUserToken( @Nullable String clientId, @Nullable String userNin ) { return new RequestBody with { // No parenthesis after 'new RequestBody' for the default constructor. audience = "audience"; withoutDefaultClientClaims = true; withoutDefaultUserClaims = true; createDPoPTokenWithDPoPProof = true; expirationParameters = new ExpirationParameters with { expirationTimeInSeconds = 300; }; clientClaimsParameters = new ClientClaimsParameters with { scope = List.of("openid", "scope"); orgnrParent = "12345"; clientId = clientId != null ? clientId : "13edc8d1-3fa2-425a-9b53-c346df79e589"; clientName = "SmokeTest"; jti = UUID.randomUUID().toString(); }; userClaimsParameters = new UserClaimsParameters with { pid = userNin != null ? userNin : "12345678912"; hprNumber = "987654"; name = "Half Badger"; givenName = "Half"; middleName = ""; familyName = "Badger"; securityLevel = "4"; assuranceLevel = "high"; amr = "pwd"; }; dPoPProofParameters = new DPoPProofParameters with { htuClaimValue = "GET"; htmClaimValue = "https://myservice/test"; } }; } The alternative, as of today, is to first instantiate each record separately and then lastly instantiate the RequestBody and return it. It's harder to read as the parameter names get lost. private static RequestBody createRequestBodyUserToken( @Nullable String clientId, @Nullable String userNin ) { var expirationParameters = new ExpirationParameters( 300 ); var clientClaimsParameters = new ClientClaimsParameters( List.of("openid","scope"), "12345", clientId != null ? clientId : "13edc8d1-3fa2-425a-9b53-c346df79e589", "SmokeTest", UUID.randomUUID().toString() ); var userClaimsParameters = new UserClaimsParameters( userNin != null ? userNin : "12345678912", "987654", "Half Badger", "Half", "", "Badger", "4", "high", "pwd" ); var dPoPProofParameters = new DPoPProofParameters( "GET", "https://myservice/test" ); return new RequestBody( "audience", true, true, true, expirationParameters, clientClaimsParameters, userClaimsParameters, dPoPProofParameters ); } I therefore hope that the default record constructor can be included with withers! It would be very useful when working with any type of api. Regards ?yvind Kvien -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 26 15:00:36 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 26 Nov 2024 10:00:36 -0500 Subject: "With" for records and the default constructor In-Reply-To: References: Message-ID: <19dce38d-64e8-4894-9b68-40667bd536a5@oracle.com> This has been well covered in several previous mails. The reason this request has not seen favorable reception is because it is actually a request for a _different_ feature -- just with a worse syntax.? What you really want is nominal (rather than positional) invocation of constructors (like, for example, `new Foo(a: 1, b: 2)`).? And we totally get that people really, really want this.? We're just not going to cram a bad version of it into the language because people want it so badly. Why this would be a bad version of nominal invocation is evident in your mail: you talk about using withers with "default" constructors, but records don't have default constructors.? And you point out why: that often there is no good default value for record components.? So in the solution you are suggestion, there is no "withing" at all; it is not deriving one record from another.? It is just trying to attach an unrelated feature to the wither proposal, because it kinda sorta looks like it (and it feels like the train is leaving the station and if we don't throw this extra feature on it, we might never get it!)? This is not the way to evolve the language. Compared to nominal invocation, the `with` version is: ?- More confusing, because there is no withing going on ?- More verbose ?- Offers no path to nominal invocation for instance members (e.g., `x.foo(a: 1)`) So it is a much worse version of "nominal invocation."? Its the sort of thing people would be happy about for about five minutes, but would likely be unhappy about thereafter. And, when we say what people really want is nominal invocation, what we mean is that they really want nominal invocation _with defaultable parameters_.? Because this is what gets rid of the builders. So yes, we deeply get that there is a pain point here.? But this is not the solution. On 11/26/2024 5:13 AM, ?yvind Kvien wrote: > I see there're no plans of including the default constructor with > withers for record creation, and a manually created static builder has > previously been suggested instead with default values in the > constructor. In my opinion ommiting the default constructor to be used > with withers would be a big mistake. > > A very common scenario in smaller services is to use simple data > structures to pass data to the api of other services and message > brokers. In these cases the data contains mostly primitive types and > (these are not domain models). Setting default values manually in the > default constructor, as suggested would be very tedious and lots of > typing. Also many times a default value is not even possible since it > could mean setting the object in a wrong initial state. Forcing the > use of default values I think is a bad approach. > > It would therefore be immensely useful if the default record > constructor could be used with withers. The compiler needs of course > to enforce that all parameters in the default constructor are set. > > Below is a real world example of how building a request body to an > internal service in my organization, used for getting access tokens > for test purposes, would look like using withers and the default > constructor. The following records describes the simple data structure > for the request body to the api of the service (which gets serialized > to json). > > ? ? record RequestBody( > ? ? ? ? ? ? String audience, > ? ? ? ? ? ? boolean withoutDefaultClientClaims, > ? ? ? ? ? ? boolean withoutDefaultUserClaims, > ? ? ? ? ? ? boolean createDPoPTokenWithDPoPProof, > ? ? ? ? ? ? ExpirationParameters expirationParameters, > ? ? ? ? ? ? ClientClaimsParameters clientClaimsParameters, > ? ? ? ? ? ? @Nullable UserClaimsParameters userClaimsParameters, > ? ? ? ? ? ? @Nullable DPoPProofParameters dPoPProofParameters) { > ? ? } > > ? ? record ExpirationParameters( > ? ? ? ? ? ? int expirationTimeInSeconds) { > ? ? } > > ? ? record ClientClaimsParameters( > ? ? ? ? ? ? List scope, > ? ? ? ? ? ? String orgnrParent, > ? ? ? ? ? ? String clientId, > ? ? ? ? ? ? String clientName, > ? ? ? ? ? ? String jti) { > ? ? } > > ? ? record UserClaimsParameters( > ? ? ? ? ? ? String pid, > ? ? ? ? ? ? String hprNumber, > ? ? ? ? ? ? String name, > ? ? ? ? ? ? String givenName, > ? ? ? ? ? ? String middleName, > ? ? ? ? ? ? String familyName, > ? ? ? ? ? ? String securityLevel, > ? ? ? ? ? ? String assuranceLevel, > ? ? ? ? ? ? String amr) { > ? ? } > > ? ? record DPoPProofParameters( > ? ? ? ? ? ? String htuClaimValue, > ? ? ? ? ? ? String htmClaimValue) { > ? ? } > > Using withers with the default constructor would look as suggested in > the method below. It's very easy to visualise the json structure that > the RequestBody record get serialized into when the parameter names > are present. > > Also there's no need to use default values which is a big win, and the > compiler enforces that all parameters are set. This way the record is > never instantiated in a wrong state. > > Note that no parenthesis are used after 'new RequestBody' for the > default constructor as a syntax suggestion. > > ? ? public static RequestBody createRequestBodyUserToken( > ? ? ? ? ? ? @Nullable String clientId, > ? ? ? ? ? ? @Nullable String userNin > ? ? ) { > ? ? ? ? return new RequestBody with { // No parenthesis after 'new > RequestBody' for the default constructor. > ? ? ? ? ? ? audience = "audience"; > ? ? ? ? ? ? withoutDefaultClientClaims = true; > ? ? ? ? ? ? withoutDefaultUserClaims = true; > ? ? ? ? ? ? createDPoPTokenWithDPoPProof = true; > ? ? ? ? ? ? expirationParameters = new ExpirationParameters with { > ? ? ? ? ? ? ? ? expirationTimeInSeconds = 300; > ? ? ? ? ? ? }; > ? ? ? ? ? ? clientClaimsParameters = new ClientClaimsParameters with { > ? ? ? ? ? ? ? ? scope = List.of("openid", "scope"); > ? ? ? ? ? ? ? ? orgnrParent = "12345"; > ? ? ? ? ? ? ? ? clientId = clientId != null ? clientId : > "13edc8d1-3fa2-425a-9b53-c346df79e589"; > ? ? ? ? ? ? ? ? clientName = "SmokeTest"; > ? ? ? ? ? ? ? ? jti = UUID.randomUUID().toString(); > ? ? ? ? ? ? }; > ? ? ? ? ? ? userClaimsParameters = new UserClaimsParameters with { > ? ? ? ? ? ? ? ? pid = userNin != null ? userNin : "12345678912"; > ? ? ? ? ? ? ? ? hprNumber = "987654"; > ? ? ? ? ? ? ? ? name = "Half Badger"; > ? ? ? ? ? ? ? ? givenName = "Half"; > ? ? ? ? ? ? ? ? middleName = ""; > ? ? ? ? ? ? ? ? familyName = "Badger"; > ? ? ? ? ? ? ? ? securityLevel = "4"; > ? ? ? ? ? ? ? ? assuranceLevel = "high"; > ? ? ? ? ? ? ? ? amr = "pwd"; > ? ? ? ? ? ? }; > ? ? ? ? ? ? ?dPoPProofParameters = new DPoPProofParameters with { > ? ? ? ? ? ? ? ? htuClaimValue = "GET"; > ? ? ? ? ? ? ? ? htmClaimValue = "https://myservice/test"; > ? ? ? ? ? ? } > ? ? ? ? }; > ? ? } > > The alternative, as of today, is to first instantiate each record > separately and then lastly instantiate the RequestBody and return it. > It's harder to read as the parameter names get lost. > > ? ? private static RequestBody createRequestBodyUserToken( > ? ? ? ? ? ? @Nullable String clientId, > ? ? ? ? ? ? @Nullable String userNin > ? ? ) { > ? ? ? ? var expirationParameters = new ExpirationParameters( > ? ? ? ? ? ? ?300 > ? ? ? ? ); > > ? ? ? ? var clientClaimsParameters = new ClientClaimsParameters( > ? ? ? ? ? ? ? ? List.of("openid","scope"), > ? ? ? ? ? ? ? ? "12345", > ? ? ? ? ? ? ? ? clientId != null ? clientId : > "13edc8d1-3fa2-425a-9b53-c346df79e589", > ? ? ? ? ? ? ? ? "SmokeTest", > ? ? ? ? ? ? ? ? UUID.randomUUID().toString() > ? ? ? ? ); > > ? ? ? ? var userClaimsParameters = new UserClaimsParameters( > ? ? ? ? ? ? ? ? userNin != null ? userNin : "12345678912", > ? ? ? ? ? ? ? ? "987654", > ? ? ? ? ? ? ? ? "Half Badger", > ? ? ? ? ? ? ? ? "Half", > ? ? ? ? ? ? ? ? "", > ? ? ? ? ? ? ? ? "Badger", > ? ? ? ? ? ? ? ? "4", > ? ? ? ? ? ? ? ? "high", > ? ? ? ? ? ? ? ? "pwd" > ? ? ? ? ); > > ? ? ? ? var dPoPProofParameters = new DPoPProofParameters( > ? ? ? ? ? ? ? ? "GET", > ? ? ? ? ? ? ? ? "https://myservice/test" > ? ? ? ? ); > > ? ? ? ? return new RequestBody( > ? ? ? ? ? ? ? ?"audience", > ? ? ? ? ? ? ? ? true, > ? ? ? ? ? ? ? ? true, > ? ? ? ? ? ? ? ? true, > ? ? ? ? ? ? ? ? expirationParameters, > ? ? ? ? ? ? ? ? clientClaimsParameters, > ? ? ? ? ? ? ? ? userClaimsParameters, > ? ? ? ? ? ? ? ? dPoPProofParameters > ? ? ? ? ); > ? ? } > > I therefore hope that the default record constructor can be included > with withers! It would be very useful when working with any type of api. > > Regards > ?yvind Kvien > -------------- next part -------------- An HTML attachment was scrubbed... URL: From oyvind at kvien.no Tue Nov 26 18:09:04 2024 From: oyvind at kvien.no (=?UTF-8?Q?=C3=98yvind_Kvien?=) Date: Tue, 26 Nov 2024 19:09:04 +0100 Subject: "With" for records and the default constructor In-Reply-To: <19dce38d-64e8-4894-9b68-40667bd536a5@oracle.com> References: <19dce38d-64e8-4894-9b68-40667bd536a5@oracle.com> Message-ID: Thank you for the answer. Yes nominal invocation and default parameter values would be a better approach. Is there a JEP drafted for this feature (I can't find one)? On Tue, Nov 26, 2024 at 4:00?PM Brian Goetz wrote: > This has been well covered in several previous mails. > > The reason this request has not seen favorable reception is because it is > actually a request for a _different_ feature -- just with a worse syntax. > What you really want is nominal (rather than positional) invocation of > constructors (like, for example, `new Foo(a: 1, b: 2)`). And we totally > get that people really, really want this. We're just not going to cram a > bad version of it into the language because people want it so badly. > > Why this would be a bad version of nominal invocation is evident in your > mail: you talk about using withers with "default" constructors, but records > don't have default constructors. And you point out why: that often there > is no good default value for record components. So in the solution you are > suggestion, there is no "withing" at all; it is not deriving one record > from another. It is just trying to attach an unrelated feature to the > wither proposal, because it kinda sorta looks like it (and it feels like > the train is leaving the station and if we don't throw this extra feature > on it, we might never get it!) This is not the way to evolve the language. > > Compared to nominal invocation, the `with` version is: > > - More confusing, because there is no withing going on > - More verbose > - Offers no path to nominal invocation for instance members (e.g., > `x.foo(a: 1)`) > > So it is a much worse version of "nominal invocation." Its the sort of > thing people would be happy about for about five minutes, but would likely > be unhappy about thereafter. > > And, when we say what people really want is nominal invocation, what we > mean is that they really want nominal invocation _with defaultable > parameters_. Because this is what gets rid of the builders. > > So yes, we deeply get that there is a pain point here. But this is not > the solution. > > > > > > > > > > > On 11/26/2024 5:13 AM, ?yvind Kvien wrote: > > I see there're no plans of including the default constructor with withers > for record creation, and a manually created static builder has previously > been suggested instead with default values in the constructor. In my > opinion ommiting the default constructor to be used with withers would be a > big mistake. > > A very common scenario in smaller services is to use simple data > structures to pass data to the api of other services and message brokers. > In these cases the data contains mostly primitive types and (these are not > domain models). Setting default values manually in the default constructor, > as suggested would be very tedious and lots of typing. Also many times a > default value is not even possible since it could mean setting the object > in a wrong initial state. Forcing the use of default values I think is a > bad approach. > > It would therefore be immensely useful if the default record constructor > could be used with withers. The compiler needs of course to enforce that > all parameters in the default constructor are set. > > Below is a real world example of how building a request body to an > internal service in my organization, used for getting access tokens for > test purposes, would look like using withers and the default constructor. > The following records describes the simple data structure for the request > body to the api of the service (which gets serialized to json). > > record RequestBody( > String audience, > boolean withoutDefaultClientClaims, > boolean withoutDefaultUserClaims, > boolean createDPoPTokenWithDPoPProof, > ExpirationParameters expirationParameters, > ClientClaimsParameters clientClaimsParameters, > @Nullable UserClaimsParameters userClaimsParameters, > @Nullable DPoPProofParameters dPoPProofParameters) { > } > > record ExpirationParameters( > int expirationTimeInSeconds) { > } > > record ClientClaimsParameters( > List scope, > String orgnrParent, > String clientId, > String clientName, > String jti) { > } > > record UserClaimsParameters( > String pid, > String hprNumber, > String name, > String givenName, > String middleName, > String familyName, > String securityLevel, > String assuranceLevel, > String amr) { > } > > record DPoPProofParameters( > String htuClaimValue, > String htmClaimValue) { > } > > Using withers with the default constructor would look as suggested in the > method below. It's very easy to visualise the json structure that the > RequestBody record get serialized into when the parameter names are present. > > Also there's no need to use default values which is a big win, and the > compiler enforces that all parameters are set. This way the record is never > instantiated in a wrong state. > > Note that no parenthesis are used after 'new RequestBody' for the default > constructor as a syntax suggestion. > > public static RequestBody createRequestBodyUserToken( > @Nullable String clientId, > @Nullable String userNin > ) { > return new RequestBody with { // No parenthesis after 'new > RequestBody' for the default constructor. > audience = "audience"; > withoutDefaultClientClaims = true; > withoutDefaultUserClaims = true; > createDPoPTokenWithDPoPProof = true; > expirationParameters = new ExpirationParameters with { > expirationTimeInSeconds = 300; > }; > clientClaimsParameters = new ClientClaimsParameters with { > scope = List.of("openid", "scope"); > orgnrParent = "12345"; > clientId = clientId != null ? clientId : > "13edc8d1-3fa2-425a-9b53-c346df79e589"; > clientName = "SmokeTest"; > jti = UUID.randomUUID().toString(); > }; > userClaimsParameters = new UserClaimsParameters with { > pid = userNin != null ? userNin : "12345678912"; > hprNumber = "987654"; > name = "Half Badger"; > givenName = "Half"; > middleName = ""; > familyName = "Badger"; > securityLevel = "4"; > assuranceLevel = "high"; > amr = "pwd"; > }; > dPoPProofParameters = new DPoPProofParameters with { > htuClaimValue = "GET"; > htmClaimValue = "https://myservice/test"; > } > }; > } > > The alternative, as of today, is to first instantiate each record > separately and then lastly instantiate the RequestBody and return it. It's > harder to read as the parameter names get lost. > > private static RequestBody createRequestBodyUserToken( > @Nullable String clientId, > @Nullable String userNin > ) { > var expirationParameters = new ExpirationParameters( > 300 > ); > > var clientClaimsParameters = new ClientClaimsParameters( > List.of("openid","scope"), > "12345", > clientId != null ? clientId : > "13edc8d1-3fa2-425a-9b53-c346df79e589", > "SmokeTest", > UUID.randomUUID().toString() > ); > > var userClaimsParameters = new UserClaimsParameters( > userNin != null ? userNin : "12345678912", > "987654", > "Half Badger", > "Half", > "", > "Badger", > "4", > "high", > "pwd" > ); > > var dPoPProofParameters = new DPoPProofParameters( > "GET", > "https://myservice/test" > ); > > return new RequestBody( > "audience", > true, > true, > true, > expirationParameters, > clientClaimsParameters, > userClaimsParameters, > dPoPProofParameters > ); > } > > I therefore hope that the default record constructor can be included with > withers! It would be very useful when working with any type of api. > > Regards > ?yvind Kvien > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Nov 26 18:24:35 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 26 Nov 2024 13:24:35 -0500 Subject: "With" for records and the default constructor In-Reply-To: References: <19dce38d-64e8-4894-9b68-40667bd536a5@oracle.com> Message-ID: <836c7715-e3ca-465b-9d28-d3fa3fa73b08@oracle.com> No, in part because we don't yet have a good enough solution, and in part because there are other higher priorities at the moment. On 11/26/2024 1:09 PM, ?yvind Kvien wrote: > Thank you for the answer. Yes nominal invocation and default parameter > values would be a better approach. Is there a JEP drafted for this > feature (I can't find one)? > > On Tue, Nov 26, 2024 at 4:00?PM Brian Goetz > wrote: > > This has been well covered in several previous mails. > > The reason this request has not seen favorable reception is > because it is actually a request for a _different_ feature -- just > with a worse syntax.? What you really want is nominal (rather than > positional) invocation of constructors (like, for example, `new > Foo(a: 1, b: 2)`). And we totally get that people really, really > want this. We're just not going to cram a bad version of it into > the language because people want it so badly. > > Why this would be a bad version of nominal invocation is evident > in your mail: you talk about using withers with "default" > constructors, but records don't have default constructors.? And > you point out why: that often there is no good default value for > record components.? So in the solution you are suggestion, there > is no "withing" at all; it is not deriving one record from > another.? It is just trying to attach an unrelated feature to the > wither proposal, because it kinda sorta looks like it (and it > feels like the train is leaving the station and if we don't throw > this extra feature on it, we might never get it!)? This is not the > way to evolve the language. > > Compared to nominal invocation, the `with` version is: > > ?- More confusing, because there is no withing going on > ?- More verbose > ?- Offers no path to nominal invocation for instance members > (e.g., `x.foo(a: 1)`) > > So it is a much worse version of "nominal invocation." Its the > sort of thing people would be happy about for about five minutes, > but would likely be unhappy about thereafter. > > And, when we say what people really want is nominal invocation, > what we mean is that they really want nominal invocation _with > defaultable parameters_.? Because this is what gets rid of the > builders. > > So yes, we deeply get that there is a pain point here. But this is > not the solution. > > > > > > > > > > > On 11/26/2024 5:13 AM, ?yvind Kvien wrote: >> I see there're no plans of including the default constructor with >> withers for record creation, and a manually created static >> builder has previously been suggested instead with default values >> in the constructor. In my opinion ommiting the default >> constructor to be used with withers would be a big mistake. >> >> A very common scenario in smaller services is to use simple data >> structures to pass data to the api of other services and message >> brokers. In these cases the data contains mostly primitive types >> and (these are not domain models). Setting default values >> manually in the default constructor, as suggested would be very >> tedious and lots of typing. Also many times a default value is >> not even possible since it could mean setting the object in a >> wrong initial state. Forcing the use of default values I think is >> a bad approach. >> >> It would therefore be immensely useful if the default record >> constructor could be used with withers. The compiler needs of >> course to enforce that all parameters in the default constructor >> are set. >> >> Below is a real world example of how building a request body to >> an internal service in my organization, used for getting access >> tokens for test purposes, would look like using withers and the >> default constructor. The following records describes the simple >> data structure for the request body to the api of the service >> (which gets serialized to json). >> >> ? ? record RequestBody( >> ? ? ? ? ? ? String audience, >> ? ? ? ? ? ? boolean withoutDefaultClientClaims, >> ? ? ? ? ? ? boolean withoutDefaultUserClaims, >> ? ? ? ? ? ? boolean createDPoPTokenWithDPoPProof, >> ? ? ? ? ? ? ExpirationParameters expirationParameters, >> ? ? ? ? ? ? ClientClaimsParameters clientClaimsParameters, >> ? ? ? ? ? ? @Nullable UserClaimsParameters userClaimsParameters, >> ? ? ? ? ? ? @Nullable DPoPProofParameters dPoPProofParameters) { >> ? ? } >> >> ? ? record ExpirationParameters( >> ? ? ? ? ? ? int expirationTimeInSeconds) { >> ? ? } >> >> ? ? record ClientClaimsParameters( >> ? ? ? ? ? ? List scope, >> ? ? ? ? ? ? String orgnrParent, >> ? ? ? ? ? ? String clientId, >> ? ? ? ? ? ? String clientName, >> ? ? ? ? ? ? String jti) { >> ? ? } >> >> ? ? record UserClaimsParameters( >> ? ? ? ? ? ? String pid, >> ? ? ? ? ? ? String hprNumber, >> ? ? ? ? ? ? String name, >> ? ? ? ? ? ? String givenName, >> ? ? ? ? ? ? String middleName, >> ? ? ? ? ? ? String familyName, >> ? ? ? ? ? ? String securityLevel, >> ? ? ? ? ? ? String assuranceLevel, >> ? ? ? ? ? ? String amr) { >> ? ? } >> >> ? ? record DPoPProofParameters( >> ? ? ? ? ? ? String htuClaimValue, >> ? ? ? ? ? ? String htmClaimValue) { >> ? ? } >> >> Using withers with the default constructor would look as >> suggested in the method below. It's very easy to visualise the >> json structure that the RequestBody record get serialized into >> when the parameter names are present. >> >> Also there's no need to use default values which is a big win, >> and the compiler enforces that all parameters are set. This way >> the record is never instantiated in a wrong state. >> >> Note that no parenthesis are used after 'new RequestBody' for the >> default constructor as a syntax suggestion. >> >> ? ? public static RequestBody createRequestBodyUserToken( >> ? ? ? ? ? ? @Nullable String clientId, >> ? ? ? ? ? ? @Nullable String userNin >> ? ? ) { >> ? ? ? ? return new RequestBody with { // No parenthesis after >> 'new RequestBody' for the default constructor. >> ? ? ? ? ? ? audience = "audience"; >> ? ? ? ? ? ? withoutDefaultClientClaims = true; >> ? ? ? ? ? ? withoutDefaultUserClaims = true; >> ? ? ? ? ? ? createDPoPTokenWithDPoPProof = true; >> ? ? ? ? ? ? expirationParameters = new ExpirationParameters with { >> ? ? ? ? ? ? ? ? expirationTimeInSeconds = 300; >> ? ? ? ? ? ? }; >> ? ? ? ? ? ? clientClaimsParameters = new ClientClaimsParameters >> with { >> ? ? ? ? ? ? ? ? scope = List.of("openid", "scope"); >> ? ? ? ? ? ? ? ? orgnrParent = "12345"; >> ? ? ? ? ? ? ? ? clientId = clientId != null ? clientId : >> "13edc8d1-3fa2-425a-9b53-c346df79e589"; >> ? ? ? ? ? ? ? ? clientName = "SmokeTest"; >> ? ? ? ? ? ? ? ? jti = UUID.randomUUID().toString(); >> ? ? ? ? ? ? }; >> ? ? ? ? ? ? userClaimsParameters = new UserClaimsParameters with { >> ? ? ? ? ? ? ? ? pid = userNin != null ? userNin : "12345678912"; >> ? ? ? ? ? ? ? ? hprNumber = "987654"; >> ? ? ? ? ? ? ? ? name = "Half Badger"; >> ? ? ? ? ? ? ? ? givenName = "Half"; >> ? ? ? ? ? ? ? ? middleName = ""; >> ? ? ? ? ? ? ? ? familyName = "Badger"; >> ? ? ? ? ? ? ? ? securityLevel = "4"; >> ? ? ? ? ? ? ? ? assuranceLevel = "high"; >> ? ? ? ? ? ? ? ? amr = "pwd"; >> ? ? ? ? ? ? }; >> ? ? ? ? ? ? ?dPoPProofParameters = new DPoPProofParameters with { >> ? ? ? ? ? ? ? ? htuClaimValue = "GET"; >> ? ? ? ? ? ? ? ? htmClaimValue = "https://myservice/test >> "; >> ? ? ? ? ? ? } >> ? ? ? ? }; >> ? ? } >> >> The alternative, as of today, is to first instantiate each record >> separately and then lastly instantiate the RequestBody and return >> it. It's harder to read as the parameter names get lost. >> >> ? ? private static RequestBody createRequestBodyUserToken( >> ? ? ? ? ? ? @Nullable String clientId, >> ? ? ? ? ? ? @Nullable String userNin >> ? ? ) { >> ? ? ? ? var expirationParameters = new ExpirationParameters( >> ? ? ? ? ? ? ?300 >> ? ? ? ? ); >> >> ? ? ? ? var clientClaimsParameters = new ClientClaimsParameters( >> ? ? ? ? ? ? ? ? List.of("openid","scope"), >> ? ? ? ? ? ? ? ? "12345", >> ? ? ? ? ? ? ? ? clientId != null ? clientId : >> "13edc8d1-3fa2-425a-9b53-c346df79e589", >> ? ? ? ? ? ? ? ? "SmokeTest", >> ? ? ? ? ? ? ? ? UUID.randomUUID().toString() >> ? ? ? ? ); >> >> ? ? ? ? var userClaimsParameters = new UserClaimsParameters( >> ? ? ? ? ? ? ? ? userNin != null ? userNin : "12345678912", >> ? ? ? ? ? ? ? ? "987654", >> ? ? ? ? ? ? ? ? "Half Badger", >> ? ? ? ? ? ? ? ? "Half", >> ? ? ? ? ? ? ? ? "", >> ? ? ? ? ? ? ? ? "Badger", >> ? ? ? ? ? ? ? ? "4", >> ? ? ? ? ? ? ? ? "high", >> ? ? ? ? ? ? ? ? "pwd" >> ? ? ? ? ); >> >> ? ? ? ? var dPoPProofParameters = new DPoPProofParameters( >> ? ? ? ? ? ? ? ? "GET", >> ? ? ? ? ? ? ? ? "https://myservice/test >> " >> ? ? ? ? ); >> >> ? ? ? ? return new RequestBody( >> ? ? ? ? ? ? ? ?"audience", >> ? ? ? ? ? ? ? ? true, >> ? ? ? ? ? ? ? ? true, >> ? ? ? ? ? ? ? ? true, >> ? ? ? ? ? ? ? ? expirationParameters, >> ? ? ? ? ? ? ? ? clientClaimsParameters, >> ? ? ? ? ? ? ? ? userClaimsParameters, >> ? ? ? ? ? ? ? ? dPoPProofParameters >> ? ? ? ? ); >> ? ? } >> >> I therefore hope that the default record constructor can be >> included with withers! It would be very useful when working with >> any type of api. >> >> Regards >> ?yvind Kvien >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Nov 26 19:41:53 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 26 Nov 2024 14:41:53 -0500 Subject: My Experience with Primitive Patterns In-Reply-To: <6a1abaa7-8231-4bc6-8571-72c1ea31d792@oracle.com> References: <6a1abaa7-8231-4bc6-8571-72c1ea31d792@oracle.com> Message-ID: Hello Brian, Anytime. This has been a long time coming for me, just been juggling a lot. I'll take a better stab at Primitive Patterns once I get some breathing room. As for regularization, I definitely hit that pot hole too of not seeing the real purpose, as you pointed out to me on Reddit. And would love to see that identification logic for pattern conflicts. Thanks for considering. On Tue, Nov 26, 2024 at 9:21?AM Brian Goetz wrote: > Thanks David. People don't always post their "I tried out the new thing > and it worked as advertised" experiences. > > One thing that is often misunderstood about this JEP is that it is more > about _regularization_ than adding a whizzy cool new feature (which often > causes people to respond with "but I don't see when I would use it" > comments.) The point of "regularization" activities like this is that > sharp edges that we've just gotten used to, can be handled within the > normal framework. When it works right, you shouldn't notice it at all. > > (Regarding switching over booleans: a useful thought experiment would be, > if we had exhaustive switch expressions over booleans in 1995, would we > even have wanted to add a ternary? (It's a thought experiment, doesn't > need to become a discussion here.)) > > error: duplicate unconditional pattern > > This error threw me for a loop when I first saw it. > > > error: neither throwing nor looping in your example > > Seriously, it is reasonable for the error message to try to identify which > patterns are unconditional. We do this with overload selection conflicts, > with type inference conflicts, and it seems reasonable to do so with > pattern conflicts as well. > > > On 11/25/2024 11:58 PM, David Alayachew wrote: > > Hello Amber Dev, > > I just wanted to post my experience with using Primitive Patterns when > trying it out in a non-trivial project. > > First off, one of the things that stood out to me immediately is how > little effort it became to isolate and properly handle edge cases. > Previously, if I wanted to provide a validation for all the different ways > that this int could be wrong, I would need a separate if statement or a > library. Typically, I would just group all of my checks into a single if > check or boolean, leading to error prone-code. But now, just a simple when > clause on a switch. Then, just throw on the right hand side for the bad > cases, and return itself on the good cases. My validation code was easier > to work with. > > Another thing that I DEEPLY appreciated was the exhaustiveness checking > for boolean. That made a lot of my code a lot more explicit, which I > appreciated. Granted, it ended up being more verbose, but I don't mind > verbosity. > > One minor annoyance, this one error message was a bit difficult to follow > without looking things up. > > error: duplicate unconditional pattern > > This error threw me for a loop when I first saw it. I NOW know it is > exactly what it means (after having done the necessary research from the > amber docs), but the error message could do with a bit more context. Maybe > clarify which case is the duplicate? If that's a hard thing to do, then no > worries. I only raise this issue in case the assumption is that the average > Java programmer will look at that error and have a good idea of what it > means without doing a non-trivial amount of reading. > > Honestly, there's not much else to write. It all just worked. I will give > Primitive Patterns another test in the future, once I have time. > > Thank you all for your time and attention. > David Alayachew > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From sarma.swaranga at gmail.com Tue Nov 26 23:56:55 2024 From: sarma.swaranga at gmail.com (Swaranga Sarma) Date: Tue, 26 Nov 2024 15:56:55 -0800 Subject: "With" for records and the default constructor In-Reply-To: <836c7715-e3ca-465b-9d28-d3fa3fa73b08@oracle.com> References: <19dce38d-64e8-4894-9b68-40667bd536a5@oracle.com> <836c7715-e3ca-465b-9d28-d3fa3fa73b08@oracle.com> Message-ID: Brian, I totally get why withers are a bad way to introduce nominal parameters for the general case. But say in future, you do come up with a solution for nominal parameters, would then withers, instead, stick out like an unnatural/redundant feature in the language that exists only to clone objects? Say we have record Rec(int a, int b) {} var one = new Record(a: 10, b:42); var two = new Record(a: one.a(), b: 24) Nominal parameters, by design, seem to also address the problems solved by withers to a degree. Sure, I still have to specify every parameter in the nominal world and it is a little more verbose so they are not directly comparable, but it still seems very close. Would like to know how you are looking at it. Regards Swaranga On Tue, Nov 26, 2024 at 10:24?AM Brian Goetz wrote: > No, in part because we don't yet have a good enough solution, and in part > because there are other higher priorities at the moment. > > > On 11/26/2024 1:09 PM, ?yvind Kvien wrote: > > Thank you for the answer. Yes nominal invocation and default parameter > values would be a better approach. Is there a JEP drafted for this feature > (I can't find one)? > > On Tue, Nov 26, 2024 at 4:00?PM Brian Goetz > wrote: > >> This has been well covered in several previous mails. >> >> The reason this request has not seen favorable reception is because it is >> actually a request for a _different_ feature -- just with a worse syntax. >> What you really want is nominal (rather than positional) invocation of >> constructors (like, for example, `new Foo(a: 1, b: 2)`). And we totally >> get that people really, really want this. We're just not going to cram a >> bad version of it into the language because people want it so badly. >> >> Why this would be a bad version of nominal invocation is evident in your >> mail: you talk about using withers with "default" constructors, but records >> don't have default constructors. And you point out why: that often there >> is no good default value for record components. So in the solution you are >> suggestion, there is no "withing" at all; it is not deriving one record >> from another. It is just trying to attach an unrelated feature to the >> wither proposal, because it kinda sorta looks like it (and it feels like >> the train is leaving the station and if we don't throw this extra feature >> on it, we might never get it!) This is not the way to evolve the language. >> >> Compared to nominal invocation, the `with` version is: >> >> - More confusing, because there is no withing going on >> - More verbose >> - Offers no path to nominal invocation for instance members (e.g., >> `x.foo(a: 1)`) >> >> So it is a much worse version of "nominal invocation." Its the sort of >> thing people would be happy about for about five minutes, but would likely >> be unhappy about thereafter. >> >> And, when we say what people really want is nominal invocation, what we >> mean is that they really want nominal invocation _with defaultable >> parameters_. Because this is what gets rid of the builders. >> >> So yes, we deeply get that there is a pain point here. But this is not >> the solution. >> >> >> >> >> >> >> >> >> >> >> On 11/26/2024 5:13 AM, ?yvind Kvien wrote: >> >> I see there're no plans of including the default constructor with withers >> for record creation, and a manually created static builder has previously >> been suggested instead with default values in the constructor. In my >> opinion ommiting the default constructor to be used with withers would be a >> big mistake. >> >> A very common scenario in smaller services is to use simple data >> structures to pass data to the api of other services and message brokers. >> In these cases the data contains mostly primitive types and (these are not >> domain models). Setting default values manually in the default constructor, >> as suggested would be very tedious and lots of typing. Also many times a >> default value is not even possible since it could mean setting the object >> in a wrong initial state. Forcing the use of default values I think is a >> bad approach. >> >> It would therefore be immensely useful if the default record constructor >> could be used with withers. The compiler needs of course to enforce that >> all parameters in the default constructor are set. >> >> Below is a real world example of how building a request body to an >> internal service in my organization, used for getting access tokens for >> test purposes, would look like using withers and the default constructor. >> The following records describes the simple data structure for the request >> body to the api of the service (which gets serialized to json). >> >> record RequestBody( >> String audience, >> boolean withoutDefaultClientClaims, >> boolean withoutDefaultUserClaims, >> boolean createDPoPTokenWithDPoPProof, >> ExpirationParameters expirationParameters, >> ClientClaimsParameters clientClaimsParameters, >> @Nullable UserClaimsParameters userClaimsParameters, >> @Nullable DPoPProofParameters dPoPProofParameters) { >> } >> >> record ExpirationParameters( >> int expirationTimeInSeconds) { >> } >> >> record ClientClaimsParameters( >> List scope, >> String orgnrParent, >> String clientId, >> String clientName, >> String jti) { >> } >> >> record UserClaimsParameters( >> String pid, >> String hprNumber, >> String name, >> String givenName, >> String middleName, >> String familyName, >> String securityLevel, >> String assuranceLevel, >> String amr) { >> } >> >> record DPoPProofParameters( >> String htuClaimValue, >> String htmClaimValue) { >> } >> >> Using withers with the default constructor would look as suggested in the >> method below. It's very easy to visualise the json structure that the >> RequestBody record get serialized into when the parameter names are present. >> >> Also there's no need to use default values which is a big win, and the >> compiler enforces that all parameters are set. This way the record is never >> instantiated in a wrong state. >> >> Note that no parenthesis are used after 'new RequestBody' for the default >> constructor as a syntax suggestion. >> >> public static RequestBody createRequestBodyUserToken( >> @Nullable String clientId, >> @Nullable String userNin >> ) { >> return new RequestBody with { // No parenthesis after 'new >> RequestBody' for the default constructor. >> audience = "audience"; >> withoutDefaultClientClaims = true; >> withoutDefaultUserClaims = true; >> createDPoPTokenWithDPoPProof = true; >> expirationParameters = new ExpirationParameters with { >> expirationTimeInSeconds = 300; >> }; >> clientClaimsParameters = new ClientClaimsParameters with { >> scope = List.of("openid", "scope"); >> orgnrParent = "12345"; >> clientId = clientId != null ? clientId : >> "13edc8d1-3fa2-425a-9b53-c346df79e589"; >> clientName = "SmokeTest"; >> jti = UUID.randomUUID().toString(); >> }; >> userClaimsParameters = new UserClaimsParameters with { >> pid = userNin != null ? userNin : "12345678912"; >> hprNumber = "987654"; >> name = "Half Badger"; >> givenName = "Half"; >> middleName = ""; >> familyName = "Badger"; >> securityLevel = "4"; >> assuranceLevel = "high"; >> amr = "pwd"; >> }; >> dPoPProofParameters = new DPoPProofParameters with { >> htuClaimValue = "GET"; >> htmClaimValue = "https://myservice/test >> >> "; >> } >> }; >> } >> >> The alternative, as of today, is to first instantiate each record >> separately and then lastly instantiate the RequestBody and return it. It's >> harder to read as the parameter names get lost. >> >> private static RequestBody createRequestBodyUserToken( >> @Nullable String clientId, >> @Nullable String userNin >> ) { >> var expirationParameters = new ExpirationParameters( >> 300 >> ); >> >> var clientClaimsParameters = new ClientClaimsParameters( >> List.of("openid","scope"), >> "12345", >> clientId != null ? clientId : >> "13edc8d1-3fa2-425a-9b53-c346df79e589", >> "SmokeTest", >> UUID.randomUUID().toString() >> ); >> >> var userClaimsParameters = new UserClaimsParameters( >> userNin != null ? userNin : "12345678912", >> "987654", >> "Half Badger", >> "Half", >> "", >> "Badger", >> "4", >> "high", >> "pwd" >> ); >> >> var dPoPProofParameters = new DPoPProofParameters( >> "GET", >> "https://myservice/test >> >> " >> ); >> >> return new RequestBody( >> "audience", >> true, >> true, >> true, >> expirationParameters, >> clientClaimsParameters, >> userClaimsParameters, >> dPoPProofParameters >> ); >> } >> >> I therefore hope that the default record constructor can be included with >> withers! It would be very useful when working with any type of api. >> >> Regards >> ?yvind Kvien >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Wed Nov 27 01:20:27 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 26 Nov 2024 20:20:27 -0500 Subject: "With" for records and the default constructor In-Reply-To: References: <19dce38d-64e8-4894-9b68-40667bd536a5@oracle.com> <836c7715-e3ca-465b-9d28-d3fa3fa73b08@oracle.com> Message-ID: Hello Swaranga, I think you are only looking at the general, most simplistic case, then pointing to that as potential evidence of why withers are not as good of a fit as nominal params. The difference that withers bring to the table is that you can run loops, statements, etc., in a wither. So, that gives you far more flexibility than nominal parameters to create the values you need, and better yet, in a new scope that keeps helper variables for only as long as needed. With that in mind, Nominal params are solving a similar, but different problem. They are simply allowing you to specify the exact variable name that you are giving this value to, as opposed to using position. It's sort of like the difference between an if statement and a ternary operator. They serve similar goals, but their Venn Diagram of intents is different. Or an if statement vs a switch statement. Again, overlap, but not the same. For more complex destructuring and restructuring, you would use a wither. For far more simplistic variable assignment (especially in cases where there isn't an old instance of the same type to extract values from), you use named parameters. Let me know if that is not clear. On Tue, Nov 26, 2024 at 6:57?PM Swaranga Sarma wrote: > Brian, I totally get why withers are a bad way to introduce nominal > parameters for the general case. But say in future, you do come up with a > solution for nominal parameters, would then withers, instead, stick out > like an unnatural/redundant feature in the language that exists only to > clone objects? Say we have > > record Rec(int a, int b) {} > > var one = new Record(a: 10, b:42); > var two = new Record(a: one.a(), b: 24) > > Nominal parameters, by design, seem to also address the problems solved by > withers to a degree. Sure, I still have to specify every parameter in the > nominal world and it is a little more verbose so they are not directly > comparable, but it still seems very close. Would like to know how you are > looking at it. > > Regards > Swaranga > > > On Tue, Nov 26, 2024 at 10:24?AM Brian Goetz > wrote: > >> No, in part because we don't yet have a good enough solution, and in part >> because there are other higher priorities at the moment. >> >> >> On 11/26/2024 1:09 PM, ?yvind Kvien wrote: >> >> Thank you for the answer. Yes nominal invocation and default parameter >> values would be a better approach. Is there a JEP drafted for this feature >> (I can't find one)? >> >> On Tue, Nov 26, 2024 at 4:00?PM Brian Goetz >> wrote: >> >>> This has been well covered in several previous mails. >>> >>> The reason this request has not seen favorable reception is because it >>> is actually a request for a _different_ feature -- just with a worse >>> syntax. What you really want is nominal (rather than positional) >>> invocation of constructors (like, for example, `new Foo(a: 1, b: 2)`). And >>> we totally get that people really, really want this. We're just not going >>> to cram a bad version of it into the language because people want it so >>> badly. >>> >>> Why this would be a bad version of nominal invocation is evident in your >>> mail: you talk about using withers with "default" constructors, but records >>> don't have default constructors. And you point out why: that often there >>> is no good default value for record components. So in the solution you are >>> suggestion, there is no "withing" at all; it is not deriving one record >>> from another. It is just trying to attach an unrelated feature to the >>> wither proposal, because it kinda sorta looks like it (and it feels like >>> the train is leaving the station and if we don't throw this extra feature >>> on it, we might never get it!) This is not the way to evolve the language. >>> >>> Compared to nominal invocation, the `with` version is: >>> >>> - More confusing, because there is no withing going on >>> - More verbose >>> - Offers no path to nominal invocation for instance members (e.g., >>> `x.foo(a: 1)`) >>> >>> So it is a much worse version of "nominal invocation." Its the sort of >>> thing people would be happy about for about five minutes, but would likely >>> be unhappy about thereafter. >>> >>> And, when we say what people really want is nominal invocation, what we >>> mean is that they really want nominal invocation _with defaultable >>> parameters_. Because this is what gets rid of the builders. >>> >>> So yes, we deeply get that there is a pain point here. But this is not >>> the solution. >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> >>> On 11/26/2024 5:13 AM, ?yvind Kvien wrote: >>> >>> I see there're no plans of including the default constructor with >>> withers for record creation, and a manually created static builder has >>> previously been suggested instead with default values in the constructor. >>> In my opinion ommiting the default constructor to be used with withers >>> would be a big mistake. >>> >>> A very common scenario in smaller services is to use simple data >>> structures to pass data to the api of other services and message brokers. >>> In these cases the data contains mostly primitive types and (these are not >>> domain models). Setting default values manually in the default constructor, >>> as suggested would be very tedious and lots of typing. Also many times a >>> default value is not even possible since it could mean setting the object >>> in a wrong initial state. Forcing the use of default values I think is a >>> bad approach. >>> >>> It would therefore be immensely useful if the default record constructor >>> could be used with withers. The compiler needs of course to enforce that >>> all parameters in the default constructor are set. >>> >>> Below is a real world example of how building a request body to an >>> internal service in my organization, used for getting access tokens for >>> test purposes, would look like using withers and the default constructor. >>> The following records describes the simple data structure for the request >>> body to the api of the service (which gets serialized to json). >>> >>> record RequestBody( >>> String audience, >>> boolean withoutDefaultClientClaims, >>> boolean withoutDefaultUserClaims, >>> boolean createDPoPTokenWithDPoPProof, >>> ExpirationParameters expirationParameters, >>> ClientClaimsParameters clientClaimsParameters, >>> @Nullable UserClaimsParameters userClaimsParameters, >>> @Nullable DPoPProofParameters dPoPProofParameters) { >>> } >>> >>> record ExpirationParameters( >>> int expirationTimeInSeconds) { >>> } >>> >>> record ClientClaimsParameters( >>> List scope, >>> String orgnrParent, >>> String clientId, >>> String clientName, >>> String jti) { >>> } >>> >>> record UserClaimsParameters( >>> String pid, >>> String hprNumber, >>> String name, >>> String givenName, >>> String middleName, >>> String familyName, >>> String securityLevel, >>> String assuranceLevel, >>> String amr) { >>> } >>> >>> record DPoPProofParameters( >>> String htuClaimValue, >>> String htmClaimValue) { >>> } >>> >>> Using withers with the default constructor would look as suggested in >>> the method below. It's very easy to visualise the json structure that the >>> RequestBody record get serialized into when the parameter names are present. >>> >>> Also there's no need to use default values which is a big win, and the >>> compiler enforces that all parameters are set. This way the record is never >>> instantiated in a wrong state. >>> >>> Note that no parenthesis are used after 'new RequestBody' for the >>> default constructor as a syntax suggestion. >>> >>> public static RequestBody createRequestBodyUserToken( >>> @Nullable String clientId, >>> @Nullable String userNin >>> ) { >>> return new RequestBody with { // No parenthesis after 'new >>> RequestBody' for the default constructor. >>> audience = "audience"; >>> withoutDefaultClientClaims = true; >>> withoutDefaultUserClaims = true; >>> createDPoPTokenWithDPoPProof = true; >>> expirationParameters = new ExpirationParameters with { >>> expirationTimeInSeconds = 300; >>> }; >>> clientClaimsParameters = new ClientClaimsParameters with { >>> scope = List.of("openid", "scope"); >>> orgnrParent = "12345"; >>> clientId = clientId != null ? clientId : >>> "13edc8d1-3fa2-425a-9b53-c346df79e589"; >>> clientName = "SmokeTest"; >>> jti = UUID.randomUUID().toString(); >>> }; >>> userClaimsParameters = new UserClaimsParameters with { >>> pid = userNin != null ? userNin : "12345678912"; >>> hprNumber = "987654"; >>> name = "Half Badger"; >>> givenName = "Half"; >>> middleName = ""; >>> familyName = "Badger"; >>> securityLevel = "4"; >>> assuranceLevel = "high"; >>> amr = "pwd"; >>> }; >>> dPoPProofParameters = new DPoPProofParameters with { >>> htuClaimValue = "GET"; >>> htmClaimValue = "https://myservice/test >>> >>> "; >>> } >>> }; >>> } >>> >>> The alternative, as of today, is to first instantiate each record >>> separately and then lastly instantiate the RequestBody and return it. It's >>> harder to read as the parameter names get lost. >>> >>> private static RequestBody createRequestBodyUserToken( >>> @Nullable String clientId, >>> @Nullable String userNin >>> ) { >>> var expirationParameters = new ExpirationParameters( >>> 300 >>> ); >>> >>> var clientClaimsParameters = new ClientClaimsParameters( >>> List.of("openid","scope"), >>> "12345", >>> clientId != null ? clientId : >>> "13edc8d1-3fa2-425a-9b53-c346df79e589", >>> "SmokeTest", >>> UUID.randomUUID().toString() >>> ); >>> >>> var userClaimsParameters = new UserClaimsParameters( >>> userNin != null ? userNin : "12345678912", >>> "987654", >>> "Half Badger", >>> "Half", >>> "", >>> "Badger", >>> "4", >>> "high", >>> "pwd" >>> ); >>> >>> var dPoPProofParameters = new DPoPProofParameters( >>> "GET", >>> "https://myservice/test >>> >>> " >>> ); >>> >>> return new RequestBody( >>> "audience", >>> true, >>> true, >>> true, >>> expirationParameters, >>> clientClaimsParameters, >>> userClaimsParameters, >>> dPoPProofParameters >>> ); >>> } >>> >>> I therefore hope that the default record constructor can be included >>> with withers! It would be very useful when working with any type of api. >>> >>> Regards >>> ?yvind Kvien >>> >>> >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From arnim.kreutzer at gmx.de Wed Nov 27 10:35:20 2024 From: arnim.kreutzer at gmx.de (Arnim Kreutzer) Date: Wed, 27 Nov 2024 11:35:20 +0100 Subject: Progress of JEP-468 Message-ID: <71c9e188-68d7-4f5c-8f5d-556369868166@gmx.de> Hi, heard of the derived record creation when listening to talks on youtube being very pleased to use this feature. Creating derived record objects could be ugly if the record has more than a couple of components. But even if the anticipation is called to be the greatest joy hopefully the wait is over soon. I don't expect the JEP 468 being integrated into JDK 24 anymore. Can you give us any update on this JEP? Would be nice. Thank you in advance arnim kreutzer From sarma.swaranga at gmail.com Wed Nov 27 20:05:37 2024 From: sarma.swaranga at gmail.com (Swaranga Sarma) Date: Wed, 27 Nov 2024 12:05:37 -0800 Subject: "With" for records and the default constructor In-Reply-To: References: <19dce38d-64e8-4894-9b68-40667bd536a5@oracle.com> <836c7715-e3ca-465b-9d28-d3fa3fa73b08@oracle.com> Message-ID: Thank you. This makes sense now. Regards Swaranga On Tue, Nov 26, 2024 at 5:21?PM David Alayachew wrote: > Hello Swaranga, > > I think you are only looking at the general, most simplistic case, then > pointing to that as potential evidence of why withers are not as good of a > fit as nominal params. > > The difference that withers bring to the table is that you can run loops, > statements, etc., in a wither. So, that gives you far more flexibility than > nominal parameters to create the values you need, and better yet, in a new > scope that keeps helper variables for only as long as needed. > > With that in mind, Nominal params are solving a similar, but different > problem. They are simply allowing you to specify the exact variable name > that you are giving this value to, as opposed to using position. > > It's sort of like the difference between an if statement and a ternary > operator. They serve similar goals, but their Venn Diagram of intents is > different. Or an if statement vs a switch statement. Again, overlap, but > not the same. > > For more complex destructuring and restructuring, you would use a wither. > > For far more simplistic variable assignment (especially in cases where > there isn't an old instance of the same type to extract values from), you > use named parameters. > > Let me know if that is not clear. > > On Tue, Nov 26, 2024 at 6:57?PM Swaranga Sarma > wrote: > >> Brian, I totally get why withers are a bad way to introduce nominal >> parameters for the general case. But say in future, you do come up with a >> solution for nominal parameters, would then withers, instead, stick out >> like an unnatural/redundant feature in the language that exists only to >> clone objects? Say we have >> >> record Rec(int a, int b) {} >> >> var one = new Record(a: 10, b:42); >> var two = new Record(a: one.a(), b: 24) >> >> Nominal parameters, by design, seem to also address the problems solved >> by withers to a degree. Sure, I still have to specify every parameter in >> the nominal world and it is a little more verbose so they are not directly >> comparable, but it still seems very close. Would like to know how you are >> looking at it. >> >> Regards >> Swaranga >> >> >> On Tue, Nov 26, 2024 at 10:24?AM Brian Goetz >> wrote: >> >>> No, in part because we don't yet have a good enough solution, and in >>> part because there are other higher priorities at the moment. >>> >>> >>> On 11/26/2024 1:09 PM, ?yvind Kvien wrote: >>> >>> Thank you for the answer. Yes nominal invocation and default parameter >>> values would be a better approach. Is there a JEP drafted for this feature >>> (I can't find one)? >>> >>> On Tue, Nov 26, 2024 at 4:00?PM Brian Goetz >>> wrote: >>> >>>> This has been well covered in several previous mails. >>>> >>>> The reason this request has not seen favorable reception is because it >>>> is actually a request for a _different_ feature -- just with a worse >>>> syntax. What you really want is nominal (rather than positional) >>>> invocation of constructors (like, for example, `new Foo(a: 1, b: 2)`). And >>>> we totally get that people really, really want this. We're just not going >>>> to cram a bad version of it into the language because people want it so >>>> badly. >>>> >>>> Why this would be a bad version of nominal invocation is evident in >>>> your mail: you talk about using withers with "default" constructors, but >>>> records don't have default constructors. And you point out why: that often >>>> there is no good default value for record components. So in the solution >>>> you are suggestion, there is no "withing" at all; it is not deriving one >>>> record from another. It is just trying to attach an unrelated feature to >>>> the wither proposal, because it kinda sorta looks like it (and it feels >>>> like the train is leaving the station and if we don't throw this extra >>>> feature on it, we might never get it!) This is not the way to evolve the >>>> language. >>>> >>>> Compared to nominal invocation, the `with` version is: >>>> >>>> - More confusing, because there is no withing going on >>>> - More verbose >>>> - Offers no path to nominal invocation for instance members (e.g., >>>> `x.foo(a: 1)`) >>>> >>>> So it is a much worse version of "nominal invocation." Its the sort of >>>> thing people would be happy about for about five minutes, but would likely >>>> be unhappy about thereafter. >>>> >>>> And, when we say what people really want is nominal invocation, what we >>>> mean is that they really want nominal invocation _with defaultable >>>> parameters_. Because this is what gets rid of the builders. >>>> >>>> So yes, we deeply get that there is a pain point here. But this is not >>>> the solution. >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> On 11/26/2024 5:13 AM, ?yvind Kvien wrote: >>>> >>>> I see there're no plans of including the default constructor with >>>> withers for record creation, and a manually created static builder has >>>> previously been suggested instead with default values in the constructor. >>>> In my opinion ommiting the default constructor to be used with withers >>>> would be a big mistake. >>>> >>>> A very common scenario in smaller services is to use simple data >>>> structures to pass data to the api of other services and message brokers. >>>> In these cases the data contains mostly primitive types and (these are not >>>> domain models). Setting default values manually in the default constructor, >>>> as suggested would be very tedious and lots of typing. Also many times a >>>> default value is not even possible since it could mean setting the object >>>> in a wrong initial state. Forcing the use of default values I think is a >>>> bad approach. >>>> >>>> It would therefore be immensely useful if the default record >>>> constructor could be used with withers. The compiler needs of course to >>>> enforce that all parameters in the default constructor are set. >>>> >>>> Below is a real world example of how building a request body to an >>>> internal service in my organization, used for getting access tokens for >>>> test purposes, would look like using withers and the default constructor. >>>> The following records describes the simple data structure for the request >>>> body to the api of the service (which gets serialized to json). >>>> >>>> record RequestBody( >>>> String audience, >>>> boolean withoutDefaultClientClaims, >>>> boolean withoutDefaultUserClaims, >>>> boolean createDPoPTokenWithDPoPProof, >>>> ExpirationParameters expirationParameters, >>>> ClientClaimsParameters clientClaimsParameters, >>>> @Nullable UserClaimsParameters userClaimsParameters, >>>> @Nullable DPoPProofParameters dPoPProofParameters) { >>>> } >>>> >>>> record ExpirationParameters( >>>> int expirationTimeInSeconds) { >>>> } >>>> >>>> record ClientClaimsParameters( >>>> List scope, >>>> String orgnrParent, >>>> String clientId, >>>> String clientName, >>>> String jti) { >>>> } >>>> >>>> record UserClaimsParameters( >>>> String pid, >>>> String hprNumber, >>>> String name, >>>> String givenName, >>>> String middleName, >>>> String familyName, >>>> String securityLevel, >>>> String assuranceLevel, >>>> String amr) { >>>> } >>>> >>>> record DPoPProofParameters( >>>> String htuClaimValue, >>>> String htmClaimValue) { >>>> } >>>> >>>> Using withers with the default constructor would look as suggested in >>>> the method below. It's very easy to visualise the json structure that the >>>> RequestBody record get serialized into when the parameter names are present. >>>> >>>> Also there's no need to use default values which is a big win, and the >>>> compiler enforces that all parameters are set. This way the record is never >>>> instantiated in a wrong state. >>>> >>>> Note that no parenthesis are used after 'new RequestBody' for the >>>> default constructor as a syntax suggestion. >>>> >>>> public static RequestBody createRequestBodyUserToken( >>>> @Nullable String clientId, >>>> @Nullable String userNin >>>> ) { >>>> return new RequestBody with { // No parenthesis after 'new >>>> RequestBody' for the default constructor. >>>> audience = "audience"; >>>> withoutDefaultClientClaims = true; >>>> withoutDefaultUserClaims = true; >>>> createDPoPTokenWithDPoPProof = true; >>>> expirationParameters = new ExpirationParameters with { >>>> expirationTimeInSeconds = 300; >>>> }; >>>> clientClaimsParameters = new ClientClaimsParameters with { >>>> scope = List.of("openid", "scope"); >>>> orgnrParent = "12345"; >>>> clientId = clientId != null ? clientId : >>>> "13edc8d1-3fa2-425a-9b53-c346df79e589"; >>>> clientName = "SmokeTest"; >>>> jti = UUID.randomUUID().toString(); >>>> }; >>>> userClaimsParameters = new UserClaimsParameters with { >>>> pid = userNin != null ? userNin : "12345678912"; >>>> hprNumber = "987654"; >>>> name = "Half Badger"; >>>> givenName = "Half"; >>>> middleName = ""; >>>> familyName = "Badger"; >>>> securityLevel = "4"; >>>> assuranceLevel = "high"; >>>> amr = "pwd"; >>>> }; >>>> dPoPProofParameters = new DPoPProofParameters with { >>>> htuClaimValue = "GET"; >>>> htmClaimValue = "https://myservice/test >>>> >>>> "; >>>> } >>>> }; >>>> } >>>> >>>> The alternative, as of today, is to first instantiate each record >>>> separately and then lastly instantiate the RequestBody and return it. It's >>>> harder to read as the parameter names get lost. >>>> >>>> private static RequestBody createRequestBodyUserToken( >>>> @Nullable String clientId, >>>> @Nullable String userNin >>>> ) { >>>> var expirationParameters = new ExpirationParameters( >>>> 300 >>>> ); >>>> >>>> var clientClaimsParameters = new ClientClaimsParameters( >>>> List.of("openid","scope"), >>>> "12345", >>>> clientId != null ? clientId : >>>> "13edc8d1-3fa2-425a-9b53-c346df79e589", >>>> "SmokeTest", >>>> UUID.randomUUID().toString() >>>> ); >>>> >>>> var userClaimsParameters = new UserClaimsParameters( >>>> userNin != null ? userNin : "12345678912", >>>> "987654", >>>> "Half Badger", >>>> "Half", >>>> "", >>>> "Badger", >>>> "4", >>>> "high", >>>> "pwd" >>>> ); >>>> >>>> var dPoPProofParameters = new DPoPProofParameters( >>>> "GET", >>>> "https://myservice/test >>>> >>>> " >>>> ); >>>> >>>> return new RequestBody( >>>> "audience", >>>> true, >>>> true, >>>> true, >>>> expirationParameters, >>>> clientClaimsParameters, >>>> userClaimsParameters, >>>> dPoPProofParameters >>>> ); >>>> } >>>> >>>> I therefore hope that the default record constructor can be included >>>> with withers! It would be very useful when working with any type of api. >>>> >>>> Regards >>>> ?yvind Kvien >>>> >>>> >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From atonita at proton.me Sat Nov 30 14:41:43 2024 From: atonita at proton.me (Aaryn Tonita) Date: Sat, 30 Nov 2024 14:41:43 +0000 Subject: Withers as procedural macros Message-ID: <-bs8wPoCNGuNXPM3974wC-67BeXQBN1qHLg2iyliDAg-ityoW6uZCw7hxyGzjzA4abLK61EVP8A-C0i1MH8vmEifmE_CK5ZGeWfgP68bhL4=@proton.me> I am very excited about withers, immutable objects are really inconvenient without such utility methods. I recently saw the distinction in the thread on '"With" for records and the default constructor' between withers and nominal constructors. I too have wished for such nominal constructors, and took notice that it isn't a priority. Fair enough. But that got me thinking about why I wanted it and how else it could be done in user land and I recalled that JEP 468 makes an excellent point for the flexibility of use site creation of new values to avoid bloat on the declaring class. This explanation feels very much like the power of procedural macros which can generate the obvious boilerplate binding at the use site. If you were in fact adding procedural macro support (through something roughly equivalent to annotation processors) then the community could fill in the gaps for nominal creation and many other features. In either case withers seem to be work that the compiler has to perform at the use site and that can either be built in or plugged in. In another thread you pointed out that you see this as a regularization feature and not a cool new feature which procedural macros certainly would be. However, the current syntax seems like it would be hard to retro fit to a procedural macro later on (if you would decide that later) while a different syntax might allow you to retrofit the work the compiler must do as a library procedural macro. I figure you must have considered procedural macros at some point. Certainly java has some code generation functionality as is but to me it does feel quite different than procedural macros (or the higher order function capabilities of python annotations) feel in other languages. Annotation processors feel like a genuinely separate build step. In fact, powerful string templating could be delivered with such a feature where the procedural could read format!("{foo}")? and close over the scoped variable foo or fail to compile when it is not in scope. So something like with!(point, x=localX, y=localY)? could yield this and more like with!(point3D, point2D, z=localZ)? for a nominal construction?. That syntax definitely feels very un-java though. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Sat Nov 30 15:22:35 2024 From: ron.pressler at oracle.com (Ron Pressler) Date: Sat, 30 Nov 2024 15:22:35 +0000 Subject: Withers as procedural macros In-Reply-To: <-bs8wPoCNGuNXPM3974wC-67BeXQBN1qHLg2iyliDAg-ityoW6uZCw7hxyGzjzA4abLK61EVP8A-C0i1MH8vmEifmE_CK5ZGeWfgP68bhL4=@proton.me> References: <-bs8wPoCNGuNXPM3974wC-67BeXQBN1qHLg2iyliDAg-ityoW6uZCw7hxyGzjzA4abLK61EVP8A-C0i1MH8vmEifmE_CK5ZGeWfgP68bhL4=@proton.me> Message-ID: <57691074-DEA7-4F68-B9B5-BC80151A3F47@oracle.com> Hi. AST macros, i.e. compile-time subroutines that take an AST (or multiple AST terms) as an argument and return an AST were made famous in programming about 60 years ago. Since then, they?ve been adopted by many languages, some of which have been very well-liked by their relatively small userbase, but none of which, to date, has managed to amass a userbase sufficient for a leading mainstream programming language. AST macros are, indeed, very powerful, but their power is their main downside. They essentially allow creating new ad-hoc syntax, and too many people find that to be a bad idea, perhaps because they think it makes approaching a new codebase more difficult. Indeed, Java?s annotation processors were carefully designed to preclude them from being used like macros. AST macros have more value in low-level languages that lack other metaprogramming capabilities, as macros can offer similar functionality in addition to introducing new syntax (which is liked by some but disliked by more). However, even in the low-level space, some modern programming languages, such as Zig, have tried to avoid macros and have shown that even their increased benefits for low-level languages can be obtained with weaker but less potentially harmful features. In any event, Java?s metaprogramming abilities are powerful and are likely to increase with the addition of things like code reflection, but given that among the majority of programmers, those who use hugely popular mainstream languages like Java, the ability to add new syntax is viewed as a downside more than an upside. For Java to adopt *general-purpose* AST macros, my guess would be that prevailing opinions among mainstream programmers would need to shift or some new and significant benefit would be found to offset the downside. However, if you like the power of macros, the Java Platform hosts one of the most beautiful and elegant programming languages that make extensive use of them ? Clojure. ? Ron > On 30 Nov 2024, at 14:41, Aaryn Tonita wrote: > > I am very excited about withers, immutable objects are really inconvenient without such utility methods. I recently saw the distinction in the thread on '"With" for records and the default constructor' between withers and nominal constructors. I too have wished for such nominal constructors, and took notice that it isn't a priority. Fair enough. > > But that got me thinking about why I wanted it and how else it could be done in user land and I recalled that JEP 468 makes an excellent point for the flexibility of use site creation of new values to avoid bloat on the declaring class. This explanation feels very much like the power of procedural macros which can generate the obvious boilerplate binding at the use site. If you were in fact adding procedural macro support (through something roughly equivalent to annotation processors) then the community could fill in the gaps for nominal creation and many other features. In either case withers seem to be work that the compiler has to perform at the use site and that can either be built in or plugged in. > > In another thread you pointed out that you see this as a regularization feature and not a cool new feature which procedural macros certainly would be. However, the current syntax seems like it would be hard to retro fit to a procedural macro later on (if you would decide that later) while a different syntax might allow you to retrofit the work the compiler must do as a library procedural macro. > > I figure you must have considered procedural macros at some point. Certainly java has some code generation functionality as is but to me it does feel quite different than procedural macros (or the higher order function capabilities of python annotations) feel in other languages. Annotation processors feel like a genuinely separate build step. In fact, powerful string templating could be delivered with such a feature where the procedural could read format!("{foo}")? and close over the scoped variable foo or fail to compile when it is not in scope. So something like with!(point, x=localX, y=localY)? could yield this and more like with!(point3D, point2D, z=localZ)? for a nominal construction?. > > That syntax definitely feels very un-java though. From brian.goetz at oracle.com Sat Nov 30 20:46:51 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 30 Nov 2024 20:46:51 +0000 Subject: Withers as procedural macros In-Reply-To: <-bs8wPoCNGuNXPM3974wC-67BeXQBN1qHLg2iyliDAg-ityoW6uZCw7hxyGzjzA4abLK61EVP8A-C0i1MH8vmEifmE_CK5ZGeWfgP68bhL4=@proton.me> References: <-bs8wPoCNGuNXPM3974wC-67BeXQBN1qHLg2iyliDAg-ityoW6uZCw7hxyGzjzA4abLK61EVP8A-C0i1MH8vmEifmE_CK5ZGeWfgP68bhL4=@proton.me> Message-ID: <4BA2A99C-1489-4135-A2E3-EE02995F6DEE@oracle.com> For completeness, I would like to point out a similarity between the reconstruction block of a wither expression and the compact constructor of a record. Both have a set of synthetic mutable locals, corresponding to the record components, and operate by mutating those locals. The difference is how the locals get their values (parameters, or getters) and what they do with them (chain to a constructor, or invoke a constructor). On Nov 30, 2024, at 9:42?AM, Aaryn Tonita wrote: ? I am very excited about withers, immutable objects are really inconvenient without such utility methods. I recently saw the distinction in the thread on '"With" for records and the default constructor' between withers and nominal constructors. I too have wished for such nominal constructors, and took notice that it isn't a priority. Fair enough. But that got me thinking about why I wanted it and how else it could be done in user land and I recalled that JEP 468 makes an excellent point for the flexibility of use site creation of new values to avoid bloat on the declaring class. This explanation feels very much like the power of procedural macros which can generate the obvious boilerplate binding at the use site. If you were in fact adding procedural macro support (through something roughly equivalent to annotation processors) then the community could fill in the gaps for nominal creation and many other features. In either case withers seem to be work that the compiler has to perform at the use site and that can either be built in or plugged in. In another thread you pointed out that you see this as a regularization feature and not a cool new feature which procedural macros certainly would be. However, the current syntax seems like it would be hard to retro fit to a procedural macro later on (if you would decide that later) while a different syntax might allow you to retrofit the work the compiler must do as a library procedural macro. I figure you must have considered procedural macros at some point. Certainly java has some code generation functionality as is but to me it does feel quite different than procedural macros (or the higher order function capabilities of python annotations) feel in other languages. Annotation processors feel like a genuinely separate build step. In fact, powerful string templating could be delivered with such a feature where the procedural could read format!("{foo}")? and close over the scoped variable foo or fail to compile when it is not in scope. So something like with!(point, x=localX, y=localY)? could yield this and more like with!(point3D, point2D, z=localZ)? for a nominal construction?. That syntax definitely feels very un-java though. -------------- next part -------------- An HTML attachment was scrubbed... URL: