From newblood17 at gmail.com Sat Jun 1 16:48:26 2024 From: newblood17 at gmail.com (mirage) Date: Sat, 1 Jun 2024 11:48:26 -0500 Subject: Jep 468 for openJDK 23 Message-ID: Hello. Is there any chances for JEP 468: derived record creation(preview), to be delivered for openJDK23? Thank you so much! -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Jun 1 17:10:48 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 1 Jun 2024 17:10:48 +0000 Subject: Jep 468 for openJDK 23 In-Reply-To: References: Message-ID: We are after rampdown for JDK 23, so there will be no new JEPs integrated. (And, before you ask, no promises for JDK 24.) > On Jun 1, 2024, at 12:48 PM, mirage wrote: > > Hello. > > Is there any chances for JEP 468: derived record creation(preview), to be delivered for openJDK23? > > Thank you so much! From zjx001202 at gmail.com Sat Jun 1 17:25:02 2024 From: zjx001202 at gmail.com (Glavo) Date: Sun, 2 Jun 2024 01:25:02 +0800 Subject: Jep 468 for openJDK 23 In-Reply-To: References: Message-ID: Hi Mirage, I think this is unlikely, as JDK 23 is about to enter Rampdown Phase One at 2024/06/06, when all features will be frozen and there will be no new JEPs targeting JDK 23. Currently JEP 468 is not proposed to target JDK 23, so it is unlikely to be available in JDK 23. Glavo On Sun, Jun 2, 2024 at 1:02?AM mirage wrote: > Hello. > > Is there any chances for JEP 468: derived record creation(preview), to be > delivered for openJDK23? > > Thank you so much! > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan.mccue at lumanu.com Sat Jun 1 22:14:08 2024 From: ethan.mccue at lumanu.com (Ethan McCue) Date: Sat, 1 Jun 2024 18:14:08 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? Message-ID: Hi all, I'm following the development of JEP 477[1] and I feel the need to question the impetus for the implicit static imports. As of now[2] any program like this void main() { println("Hello, world"); } Is equivalent to import static java.io.IO.print; import static java.io.IO.println; import static java.io.IO.writeln; import module java.base; final class Main { void main() { println("Hello, world"); } } Where all the methods in java.io.IO delegate to newly added equivalent methods in java.io.Console.[3] Aside from muddying that API up (now there is readln and readLine + println and printLine which...what) I'm still concerned on how those implicit imports will affect the transition to named classes. Assume we start a student out here void main() { println("Hello, world"); } You can get through conditionals, loops, variables, methods, and return types before touching classes or access specifiers. int compute() { int total = 0; for (int i = 0; i < 10; i++) { total += i; } return total; } void main() { println("Hello: " + compute()); } You can even talk about records and enums in a hand wavey way. Enums are "one of these options", Records are "if you want to return two things." enum Pirate { BLACKBEARD, OTHER } record Pos(int x, int y) {} Pos treasure(Pirate pirate) { switch (pirate) { case BLACKBEARD -> return new Pos(5, 5); case OTHER -> return new Pos(0, 0); } } void main() { println(treasure(Pirate.OTHER)); } So it is reasonable for a student to have made a relatively complex program before having to get to that point, but I think you do need to explain what exactly is going on with the anonymous main class when you introduce multi file programs. As originally pitched, this transition would have just meant wrapping the whole program in class Main {}, but now to make the transition from void main() { // Arbitrary code } to class Main { void main() { // Arbitrary code } } In a robust way, you need to add those imports to the top. You can skip the final since the semantics of extension haven't been relevant yet. import static java.io.IO.*; import module java.base; class Main { void main() { // Arbitrary code } } My gripe is that the concepts introduced here - static, module, and * imports - would have had no place to be introduced earlier. If you have folks write static methods on inner classes, the metaphor of a "global field" that otherwise exists in the simple-main world goes away. // For every program up until this, they could freely access this from anywhere int count = 0; // And they could freely make an instance of any inner class class Other {} class Pos { static Pos of(int x, int y) { // So the rules they are used to don't apply anymore // and the explanation as to why really lies *after* they understand // what an anonymous main class is and does // ... } } void main() { // ... } If you have folks *use* static methods that is fine - the hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure out how to topologically sort topics such that import static makes any sense before going through multi-file programs. I have a similar concern with import module, but that can be hand waved as "gets you everything from this library" so I am less concerned. Still don't fully understand the desire to have it be implicit, but less concerned. Just as a hypothetical, say java.io.IO was java.lang.IO. No new import rules needed and now IO is available to all programs. void main() { IO.printLine("Hello, world"); } While this does introduce something not explained - static method access - it feels easier to hand-wave away than import static java.io.IO.*; would be. It calls the printLine method, it comes from IO. IO means "Input/Output". That's a workable metaphor and can be glanced over in the same way as Math.max The transition from here to classes can again be "it's the same as if you had class Main {} around it" instead of "it's the same as if you had class Main {} around it and these imports. A static import imports a static method. A static method is a method attached to the class itself instead of the instance.... etc." Also, IO. feels like a different kind of unexplained boilerplate than public static void main(String[] args) {}. There is a lot of ground you need to cover before access modifiers, static methods, or command line arguments make any sort of sense. When you have someone write IO they are in that moment producing output and will soon take input. What am I overlooking? [1]: https://openjdk.org/jeps/477 [2]: https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html [3] https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Sat Jun 1 22:16:03 2024 From: ethan at mccue.dev (Ethan McCue) Date: Sat, 1 Jun 2024 18:16:03 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? Message-ID: Hi all, I'm following the development of JEP 477[1] and I feel the need to question the impetus for the implicit static imports. As of now[2] any program like this void main() { println("Hello, world"); } Is equivalent to import static java.io.IO.print; import static java.io.IO.println; import static java.io.IO.writeln; import module java.base; final class Main { void main() { println("Hello, world"); } } Where all the methods in java.io.IO delegate to newly added equivalent methods in java.io.Console.[3] Aside from muddying that API up (now there is readln and readLine + println and printLine which...what) I'm still concerned on how those implicit imports will affect the transition to named classes. Assume we start a student out here void main() { println("Hello, world"); } You can get through conditionals, loops, variables, methods, and return types before touching classes or access specifiers. int compute() { int total = 0; for (int i = 0; i < 10; i++) { total += i; } return total; } void main() { println("Hello: " + compute()); } You can even talk about records and enums in a hand wavey way. Enums are "one of these options", Records are "if you want to return two things." enum Pirate { BLACKBEARD, OTHER } record Pos(int x, int y) {} Pos treasure(Pirate pirate) { switch (pirate) { case BLACKBEARD -> return new Pos(5, 5); case OTHER -> return new Pos(0, 0); } } void main() { println(treasure(Pirate.OTHER)); } So it is reasonable for a student to have made a relatively complex program before having to get to that point, but I think you do need to explain what exactly is going on with the anonymous main class when you introduce multi file programs. As originally pitched, this transition would have just meant wrapping the whole program in class Main {}, but now to make the transition from void main() { // Arbitrary code } to class Main { void main() { // Arbitrary code } } In a robust way, you need to add those imports to the top. You can skip the final since the semantics of extension haven't been relevant yet. import static java.io.IO.*; import module java.base; class Main { void main() { // Arbitrary code } } My gripe is that the concepts introduced here - static, module, and * imports - would have had no place to be introduced earlier. If you have folks write static methods on inner classes, the metaphor of a "global field" that otherwise exists in the simple-main world goes away. // For every program up until this, they could freely access this from anywhere int count = 0; // And they could freely make an instance of any inner class class Other {} class Pos { static Pos of(int x, int y) { // So the rules they are used to don't apply anymore // and the explanation as to why really lies *after* they understand // what an anonymous main class is and does // ... } } void main() { // ... } If you have folks *use* static methods that is fine - the hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure out how to topologically sort topics such that import static makes any sense before going through multi-file programs. I have a similar concern with import module, but that can be hand waved as "gets you everything from this library" so I am less concerned. Still don't fully understand the desire to have it be implicit, but less concerned. Just as a hypothetical, say java.io.IO was java.lang.IO. No new import rules needed and now IO is available to all programs. void main() { IO.printLine("Hello, world"); } While this does introduce something not explained - static method access - it feels easier to hand-wave away than import static java.io.IO.*; would be. It calls the printLine method, it comes from IO. IO means "Input/Output". That's a workable metaphor and can be glanced over in the same way as Math.max The transition from here to classes can again be "it's the same as if you had class Main {} around it" instead of "it's the same as if you had class Main {} around it and these imports. A static import imports a static method. A static method is a method attached to the class itself instead of the instance.... etc." Also, IO. feels like a different kind of unexplained boilerplate than public static void main(String[] args) {}. There is a lot of ground you need to cover before access modifiers, static methods, or command line arguments make any sort of sense. When you have someone write IO they are in that moment producing output and will soon take input. What am I overlooking? [1]: https://openjdk.org/jeps/477 [2]: https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html [3] https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Sat Jun 1 22:41:04 2024 From: ron.pressler at oracle.com (Ron Pressler) Date: Sat, 1 Jun 2024 22:41:04 +0000 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: Hi. Without getting into the merits of not implicitly importing anything, let me just point out that the following is a valid program under the current JEP: void main() { IO.println(?Hello, world!?); } As is this one: import module java.base; void main() { IO.println(?Hello, world!?); } In other words, the fact that there are implicit imports doesn?t mean that you can?t ignore them or add them explicitly. if you find teaching this to be easier, you can. So even if you don?t find implicit imports helpful, they may be helpful to other teachers, who may not want to start with some import incantation that necessarily implies some programming-in-the-large concept. But I think that your unease mostly stems from the extra magic that implicit classes enjoy, and which isn?t strictly necessitated by their primary quality of being implicitly declared classes, and that extra magic differentiates them from regular compilation units along some other axis; is that right? ? Ron > On 1 Jun 2024, at 23:14, Ethan McCue wrote: > > Hi all, > > I'm following the development of JEP 477[1] and I feel the need to question the impetus for the implicit static imports. > > As of now[2] any program like this > > void main() { > println("Hello, world"); > } > > Is equivalent to > > import static java.io.IO.print; > import static java.io.IO.println; > import static java.io.IO.writeln; > > import module java.base; > > final class Main { > void main() { > println("Hello, world"); > } > } > > Where all the methods in java.io.IO delegate to newly added equivalent methods in java.io.Console.[3] > > Aside from muddying that API up (now there is readln and readLine + println and printLine which...what) I'm still concerned on how those implicit imports will affect the transition to named classes. > > Assume we start a student out here > > void main() { > println("Hello, world"); > } > > You can get through conditionals, loops, variables, methods, and return types before touching classes or access specifiers. > > int compute() { > int total = 0; > for (int i = 0; i < 10; i++) { > total += i; > } > return total; > } > > void main() { > println("Hello: " + compute()); > } > > You can even talk about records and enums in a hand wavey way. Enums are "one of these options", Records are "if you want to return two things." > > enum Pirate { > BLACKBEARD, > OTHER > } > > record Pos(int x, int y) {} > > Pos treasure(Pirate pirate) { > switch (pirate) { > case BLACKBEARD -> > return new Pos(5, 5); > case OTHER -> > return new Pos(0, 0); > } > } > > void main() { > println(treasure(Pirate.OTHER)); > } > > So it is reasonable for a student to have made a relatively complex program before having to get to that point, but I think you do need to explain what exactly is going on with the anonymous main class when you introduce multi file programs. > > As originally pitched, this transition would have just meant wrapping the whole program in class Main {}, but now to make the transition from > > void main() { > // Arbitrary code > } > > to > > class Main { > void main() { > // Arbitrary code > } > } > > In a robust way, you need to add those imports to the top. You can skip the final since the semantics of extension haven't been relevant yet. > > import static java.io.IO.*; > > import module java.base; > > class Main { > void main() { > // Arbitrary code > } > } > > My gripe is that the concepts introduced here - static, module, and * imports - would have had no place to be introduced earlier. > > If you have folks write static methods on inner classes, the metaphor of a "global field" that otherwise exists in the simple-main world goes away. > > // For every program up until this, they could freely access this from anywhere > int count = 0; > > // And they could freely make an instance of any inner class > class Other {} > > class Pos { > static Pos of(int x, int y) { > // So the rules they are used to don't apply anymore > // and the explanation as to why really lies *after* they understand > // what an anonymous main class is and does > // ... > } > } > > void main() { > // ... > } > > If you have folks *use* static methods that is fine - the hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure out how to topologically sort topics such that import static makes any sense before going through multi-file programs. > > I have a similar concern with import module, but that can be hand waved as "gets you everything from this library" so I am less concerned. Still don't fully understand the desire to have it be implicit, but less concerned. > > Just as a hypothetical, say java.io.IO was java.lang.IO. No new import rules needed and now IO is available to all programs. > > void main() { > IO.printLine("Hello, world"); > } > > While this does introduce something not explained - static method access - it feels easier to hand-wave away than import static java.io.IO.*; would be. It calls the printLine method, it comes from IO. IO means "Input/Output". That's a workable metaphor and can be glanced over in the same way as Math.max > > The transition from here to classes can again be "it's the same as if you had class Main {} around it" instead of "it's the same as if you had class Main {} around it and these imports. A static import imports a static method. A static method is a method attached to the class itself instead of the instance.... etc." > > Also, IO. feels like a different kind of unexplained boilerplate than public static void main(String[] args) {}. There is a lot of ground you need to cover before access modifiers, static methods, or command line arguments make any sort of sense. When you have someone write IO they are in that moment producing output and will soon take input. > > What am I overlooking? > > > [1]: https://openjdk.org/jeps/477 > [2]: https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html > [3] https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) From davidalayachew at gmail.com Sat Jun 1 23:55:23 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 1 Jun 2024 19:55:23 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: Maybe I am missing something, but a static import seems like the natural next step to teaching a student who made it that far. If a student feels the need to break out of the bounds of an implicitly declared class, they must first understand what a class is going to save them from. In this case, it is allowing code they right to be reused else where. I am only a tutor, not a full-blown teacher, but when tutoring folks in this exact subject, I would always start by taking one of their projects, ask them to tweak in a subtle, but difficult way, then let them run headfirst into the problem. Once they start to feel the strain, I would introduce them to the concepts of classes. I would start by showing them how to create a simple pure function, similar to java.lang.Math. From there, we would make a few pure functions that are unrelated to their current task, and then have them get comfortable with that concept. Then, we would make another pure function that is being done repeatedly in their "JumboClass1" and "JumboClass2", and then have them get comfortable using this helper method in both versions. In my mind, this is naturally where the student would discover that they need to do a static import. In which case, they are in the middle of understanding a class, they understand imports, and they understand static methods fairly well. So, a static import has been well prepared for them. For me, this is a neat and clean introduction. Maybe I am missing something? On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler wrote: > Hi. > > Without getting into the merits of not implicitly importing anything, let > me just point out that the following is a valid program under the current > JEP: > > void main() { > IO.println(?Hello, world!?); > } > > As is this one: > > import module java.base; > > void main() { > IO.println(?Hello, world!?); > } > > In other words, the fact that there are implicit imports doesn?t mean that > you can?t ignore them or add them explicitly. if you find teaching this to > be easier, you can. So even if you don?t find implicit imports helpful, > they may be helpful to other teachers, who may not want to start with some > import incantation that necessarily implies some programming-in-the-large > concept. > > But I think that your unease mostly stems from the extra magic that > implicit classes enjoy, and which isn?t strictly necessitated by their > primary quality of being implicitly declared classes, and that extra magic > differentiates them from regular compilation units along some other axis; > is that right? > > ? Ron > > > > > On 1 Jun 2024, at 23:14, Ethan McCue wrote: > > > > Hi all, > > > > I'm following the development of JEP 477[1] and I feel the need to > question the impetus for the implicit static imports. > > > > As of now[2] any program like this > > > > void main() { > > println("Hello, world"); > > } > > > > Is equivalent to > > > > import static java.io.IO.print; > > import static java.io.IO.println; > > import static java.io.IO.writeln; > > > > import module java.base; > > > > final class Main { > > void main() { > > println("Hello, world"); > > } > > } > > > > Where all the methods in java.io.IO delegate to newly added equivalent > methods in java.io.Console.[3] > > > > Aside from muddying that API up (now there is readln and readLine + > println and printLine which...what) I'm still concerned on how those > implicit imports will affect the transition to named classes. > > > > Assume we start a student out here > > > > void main() { > > println("Hello, world"); > > } > > > > You can get through conditionals, loops, variables, methods, and return > types before touching classes or access specifiers. > > > > int compute() { > > int total = 0; > > for (int i = 0; i < 10; i++) { > > total += i; > > } > > return total; > > } > > > > void main() { > > println("Hello: " + compute()); > > } > > > > You can even talk about records and enums in a hand wavey way. Enums are > "one of these options", Records are "if you want to return two things." > > > > enum Pirate { > > BLACKBEARD, > > OTHER > > } > > > > record Pos(int x, int y) {} > > > > Pos treasure(Pirate pirate) { > > switch (pirate) { > > case BLACKBEARD -> > > return new Pos(5, 5); > > case OTHER -> > > return new Pos(0, 0); > > } > > } > > > > void main() { > > println(treasure(Pirate.OTHER)); > > } > > > > So it is reasonable for a student to have made a relatively complex > program before having to get to that point, but I think you do need to > explain what exactly is going on with the anonymous main class when you > introduce multi file programs. > > > > As originally pitched, this transition would have just meant wrapping > the whole program in class Main {}, but now to make the transition from > > > > void main() { > > // Arbitrary code > > } > > > > to > > > > class Main { > > void main() { > > // Arbitrary code > > } > > } > > > > In a robust way, you need to add those imports to the top. You can skip > the final since the semantics of extension haven't been relevant yet. > > > > import static java.io.IO.*; > > > > import module java.base; > > > > class Main { > > void main() { > > // Arbitrary code > > } > > } > > > > My gripe is that the concepts introduced here - static, module, and * > imports - would have had no place to be introduced earlier. > > > > If you have folks write static methods on inner classes, the metaphor of > a "global field" that otherwise exists in the simple-main world goes away. > > > > // For every program up until this, they could freely access this from > anywhere > > int count = 0; > > > > // And they could freely make an instance of any inner class > > class Other {} > > > > class Pos { > > static Pos of(int x, int y) { > > // So the rules they are used to don't apply anymore > > // and the explanation as to why really lies *after* they > understand > > // what an anonymous main class is and does > > // ... > > } > > } > > > > void main() { > > // ... > > } > > > > If you have folks *use* static methods that is fine - the hand-waving of > Math.max doesn't seem to trip anyone up - but I can't figure out how to > topologically sort topics such that import static makes any sense before > going through multi-file programs. > > > > I have a similar concern with import module, but that can be hand waved > as "gets you everything from this library" so I am less concerned. Still > don't fully understand the desire to have it be implicit, but less > concerned. > > > > Just as a hypothetical, say java.io.IO was java.lang.IO. No new import > rules needed and now IO is available to all programs. > > > > void main() { > > IO.printLine("Hello, world"); > > } > > > > While this does introduce something not explained - static method access > - it feels easier to hand-wave away than import static java.io.IO.*; would > be. It calls the printLine method, it comes from IO. IO means > "Input/Output". That's a workable metaphor and can be glanced over in the > same way as Math.max > > > > The transition from here to classes can again be "it's the same as if > you had class Main {} around it" instead of "it's the same as if you had > class Main {} around it and these imports. A static import imports a static > method. A static method is a method attached to the class itself instead of > the instance.... etc." > > > > Also, IO. feels like a different kind of unexplained boilerplate than > public static void main(String[] args) {}. There is a lot of ground you > need to cover before access modifiers, static methods, or command line > arguments make any sort of sense. When you have someone write IO they are > in that moment producing output and will soon take input. > > > > What am I overlooking? > > > > > > [1]: https://openjdk.org/jeps/477 > > [2]: > https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html > > [3] > https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Sun Jun 2 01:28:36 2024 From: ethan at mccue.dev (Ethan McCue) Date: Sat, 1 Jun 2024 21:28:36 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: I am also basically just a tutor, just unpaid. I think the problem is that without static "global" fields are always accessible and all the nested classes in their program are instansable from anywhere. Once you have a static method, a method on a record, a method on an enum, or a static inner class this is no longer the case. Trite example of the mechanics I'm referencing: int count = 0; class A { void b() { count++; c(); new D(); } } void c() { println("" + count); } class D {} void main() { new A().b(); } So when you show static methods, I think you need to understand why count++; and c(); and new D(); would not function. That requires knowing that you are actually in an anonymous class. That requires we get past import static java.io.IO. That's the loop. On Sat, Jun 1, 2024, 8:59 PM David Alayachew wrote: > Maybe I am missing something, but a static import seems like the natural > next step to teaching a student who made it that far. > > If a student feels the need to break out of the bounds of an implicitly > declared class, they must first understand what a class is going to save > them from. In this case, it is allowing code they right to be reused else > where. > > I am only a tutor, not a full-blown teacher, but when tutoring folks in > this exact subject, I would always start by taking one of their projects, > ask them to tweak in a subtle, but difficult way, then let them run > headfirst into the problem. Once they start to feel the strain, I would > introduce them to the concepts of classes. > > I would start by showing them how to create a simple pure function, > similar to java.lang.Math. From there, we would make a few pure functions > that are unrelated to their current task, and then have them get > comfortable with that concept. > > Then, we would make another pure function that is being done repeatedly in > their "JumboClass1" and "JumboClass2", and then have them get comfortable > using this helper method in both versions. > > In my mind, this is naturally where the student would discover that they > need to do a static import. In which case, they are in the middle of > understanding a class, they understand imports, and they understand static > methods fairly well. So, a static import has been well prepared for them. > > For me, this is a neat and clean introduction. Maybe I am missing > something? > > On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler > wrote: > >> Hi. >> >> Without getting into the merits of not implicitly importing anything, let >> me just point out that the following is a valid program under the current >> JEP: >> >> void main() { >> IO.println(?Hello, world!?); >> } >> >> As is this one: >> >> import module java.base; >> >> void main() { >> IO.println(?Hello, world!?); >> } >> >> In other words, the fact that there are implicit imports doesn?t mean >> that you can?t ignore them or add them explicitly. if you find teaching >> this to be easier, you can. So even if you don?t find implicit imports >> helpful, they may be helpful to other teachers, who may not want to start >> with some import incantation that necessarily implies some >> programming-in-the-large concept. >> >> But I think that your unease mostly stems from the extra magic that >> implicit classes enjoy, and which isn?t strictly necessitated by their >> primary quality of being implicitly declared classes, and that extra magic >> differentiates them from regular compilation units along some other axis; >> is that right? >> >> ? Ron >> >> >> >> > On 1 Jun 2024, at 23:14, Ethan McCue wrote: >> > >> > Hi all, >> > >> > I'm following the development of JEP 477[1] and I feel the need to >> question the impetus for the implicit static imports. >> > >> > As of now[2] any program like this >> > >> > void main() { >> > println("Hello, world"); >> > } >> > >> > Is equivalent to >> > >> > import static java.io.IO.print; >> > import static java.io.IO.println; >> > import static java.io.IO.writeln; >> > >> > import module java.base; >> > >> > final class Main { >> > void main() { >> > println("Hello, world"); >> > } >> > } >> > >> > Where all the methods in java.io.IO delegate to newly added equivalent >> methods in java.io.Console.[3] >> > >> > Aside from muddying that API up (now there is readln and readLine + >> println and printLine which...what) I'm still concerned on how those >> implicit imports will affect the transition to named classes. >> > >> > Assume we start a student out here >> > >> > void main() { >> > println("Hello, world"); >> > } >> > >> > You can get through conditionals, loops, variables, methods, and return >> types before touching classes or access specifiers. >> > >> > int compute() { >> > int total = 0; >> > for (int i = 0; i < 10; i++) { >> > total += i; >> > } >> > return total; >> > } >> > >> > void main() { >> > println("Hello: " + compute()); >> > } >> > >> > You can even talk about records and enums in a hand wavey way. Enums >> are "one of these options", Records are "if you want to return two things." >> > >> > enum Pirate { >> > BLACKBEARD, >> > OTHER >> > } >> > >> > record Pos(int x, int y) {} >> > >> > Pos treasure(Pirate pirate) { >> > switch (pirate) { >> > case BLACKBEARD -> >> > return new Pos(5, 5); >> > case OTHER -> >> > return new Pos(0, 0); >> > } >> > } >> > >> > void main() { >> > println(treasure(Pirate.OTHER)); >> > } >> > >> > So it is reasonable for a student to have made a relatively complex >> program before having to get to that point, but I think you do need to >> explain what exactly is going on with the anonymous main class when you >> introduce multi file programs. >> > >> > As originally pitched, this transition would have just meant wrapping >> the whole program in class Main {}, but now to make the transition from >> > >> > void main() { >> > // Arbitrary code >> > } >> > >> > to >> > >> > class Main { >> > void main() { >> > // Arbitrary code >> > } >> > } >> > >> > In a robust way, you need to add those imports to the top. You can skip >> the final since the semantics of extension haven't been relevant yet. >> > >> > import static java.io.IO.*; >> > >> > import module java.base; >> > >> > class Main { >> > void main() { >> > // Arbitrary code >> > } >> > } >> > >> > My gripe is that the concepts introduced here - static, module, and * >> imports - would have had no place to be introduced earlier. >> > >> > If you have folks write static methods on inner classes, the metaphor >> of a "global field" that otherwise exists in the simple-main world goes >> away. >> > >> > // For every program up until this, they could freely access this from >> anywhere >> > int count = 0; >> > >> > // And they could freely make an instance of any inner class >> > class Other {} >> > >> > class Pos { >> > static Pos of(int x, int y) { >> > // So the rules they are used to don't apply anymore >> > // and the explanation as to why really lies *after* they >> understand >> > // what an anonymous main class is and does >> > // ... >> > } >> > } >> > >> > void main() { >> > // ... >> > } >> > >> > If you have folks *use* static methods that is fine - the hand-waving >> of Math.max doesn't seem to trip anyone up - but I can't figure out how to >> topologically sort topics such that import static makes any sense before >> going through multi-file programs. >> > >> > I have a similar concern with import module, but that can be hand waved >> as "gets you everything from this library" so I am less concerned. Still >> don't fully understand the desire to have it be implicit, but less >> concerned. >> > >> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new import >> rules needed and now IO is available to all programs. >> > >> > void main() { >> > IO.printLine("Hello, world"); >> > } >> > >> > While this does introduce something not explained - static method >> access - it feels easier to hand-wave away than import static java.io.IO.*; >> would be. It calls the printLine method, it comes from IO. IO means >> "Input/Output". That's a workable metaphor and can be glanced over in the >> same way as Math.max >> > >> > The transition from here to classes can again be "it's the same as if >> you had class Main {} around it" instead of "it's the same as if you had >> class Main {} around it and these imports. A static import imports a static >> method. A static method is a method attached to the class itself instead of >> the instance.... etc." >> > >> > Also, IO. feels like a different kind of unexplained boilerplate than >> public static void main(String[] args) {}. There is a lot of ground you >> need to cover before access modifiers, static methods, or command line >> arguments make any sort of sense. When you have someone write IO they are >> in that moment producing output and will soon take input. >> > >> > What am I overlooking? >> > >> > >> > [1]: https://openjdk.org/jeps/477 >> > [2]: >> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >> > [3] >> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Jun 2 02:46:30 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 1 Jun 2024 22:46:30 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: Heh, we're both unpaid :P I tutor folks from my university and church, as well as a couple of friends and family. Slowing down now though because of workload at work. But back to the point. > I think the problem is that without static "global" > fields are always accessible and all the nested classes > in their program are instansable from anywhere. > > Once you have a static method, a method on a record, a > method on an enum, or a static inner class this is no > longer the case. Apologies, maybe I missed the memo. I have been out of touch with this (and all other JEP's) because of my insane workload for the past couple of months. Why would adding a static method change any of the logic? I took your example in Java 22, added a static method, and everything worked dandy. Am I missing something? On Sat, Jun 1, 2024 at 9:28?PM Ethan McCue wrote: > I am also basically just a tutor, just unpaid. > > I think the problem is that without static "global" fields are always > accessible and all the nested classes in their program are instansable from > anywhere. > > Once you have a static method, a method on a record, a method on an enum, > or a static inner class this is no longer the case. > > Trite example of the mechanics I'm referencing: > > int count = 0; > > class A { > void b() { > count++; > c(); > new D(); > } > } > > void c() { > println("" + count); > } > > class D {} > > void main() { > new A().b(); > } > > So when you show static methods, I think you need to understand why > count++; and c(); and new D(); would not function. > > That requires knowing that you are actually in an anonymous class. That > requires we get past import static java.io.IO. That's the loop. > > > > On Sat, Jun 1, 2024, 8:59 PM David Alayachew > wrote: > >> Maybe I am missing something, but a static import seems like the natural >> next step to teaching a student who made it that far. >> >> If a student feels the need to break out of the bounds of an implicitly >> declared class, they must first understand what a class is going to save >> them from. In this case, it is allowing code they right to be reused else >> where. >> >> I am only a tutor, not a full-blown teacher, but when tutoring folks in >> this exact subject, I would always start by taking one of their projects, >> ask them to tweak in a subtle, but difficult way, then let them run >> headfirst into the problem. Once they start to feel the strain, I would >> introduce them to the concepts of classes. >> >> I would start by showing them how to create a simple pure function, >> similar to java.lang.Math. From there, we would make a few pure functions >> that are unrelated to their current task, and then have them get >> comfortable with that concept. >> >> Then, we would make another pure function that is being done repeatedly >> in their "JumboClass1" and "JumboClass2", and then have them get >> comfortable using this helper method in both versions. >> >> In my mind, this is naturally where the student would discover that they >> need to do a static import. In which case, they are in the middle of >> understanding a class, they understand imports, and they understand static >> methods fairly well. So, a static import has been well prepared for them. >> >> For me, this is a neat and clean introduction. Maybe I am missing >> something? >> >> On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler >> wrote: >> >>> Hi. >>> >>> Without getting into the merits of not implicitly importing anything, >>> let me just point out that the following is a valid program under the >>> current JEP: >>> >>> void main() { >>> IO.println(?Hello, world!?); >>> } >>> >>> As is this one: >>> >>> import module java.base; >>> >>> void main() { >>> IO.println(?Hello, world!?); >>> } >>> >>> In other words, the fact that there are implicit imports doesn?t mean >>> that you can?t ignore them or add them explicitly. if you find teaching >>> this to be easier, you can. So even if you don?t find implicit imports >>> helpful, they may be helpful to other teachers, who may not want to start >>> with some import incantation that necessarily implies some >>> programming-in-the-large concept. >>> >>> But I think that your unease mostly stems from the extra magic that >>> implicit classes enjoy, and which isn?t strictly necessitated by their >>> primary quality of being implicitly declared classes, and that extra magic >>> differentiates them from regular compilation units along some other axis; >>> is that right? >>> >>> ? Ron >>> >>> >>> >>> > On 1 Jun 2024, at 23:14, Ethan McCue wrote: >>> > >>> > Hi all, >>> > >>> > I'm following the development of JEP 477[1] and I feel the need to >>> question the impetus for the implicit static imports. >>> > >>> > As of now[2] any program like this >>> > >>> > void main() { >>> > println("Hello, world"); >>> > } >>> > >>> > Is equivalent to >>> > >>> > import static java.io.IO.print; >>> > import static java.io.IO.println; >>> > import static java.io.IO.writeln; >>> > >>> > import module java.base; >>> > >>> > final class Main { >>> > void main() { >>> > println("Hello, world"); >>> > } >>> > } >>> > >>> > Where all the methods in java.io.IO delegate to newly added >>> equivalent methods in java.io.Console.[3] >>> > >>> > Aside from muddying that API up (now there is readln and readLine + >>> println and printLine which...what) I'm still concerned on how those >>> implicit imports will affect the transition to named classes. >>> > >>> > Assume we start a student out here >>> > >>> > void main() { >>> > println("Hello, world"); >>> > } >>> > >>> > You can get through conditionals, loops, variables, methods, and >>> return types before touching classes or access specifiers. >>> > >>> > int compute() { >>> > int total = 0; >>> > for (int i = 0; i < 10; i++) { >>> > total += i; >>> > } >>> > return total; >>> > } >>> > >>> > void main() { >>> > println("Hello: " + compute()); >>> > } >>> > >>> > You can even talk about records and enums in a hand wavey way. Enums >>> are "one of these options", Records are "if you want to return two things." >>> > >>> > enum Pirate { >>> > BLACKBEARD, >>> > OTHER >>> > } >>> > >>> > record Pos(int x, int y) {} >>> > >>> > Pos treasure(Pirate pirate) { >>> > switch (pirate) { >>> > case BLACKBEARD -> >>> > return new Pos(5, 5); >>> > case OTHER -> >>> > return new Pos(0, 0); >>> > } >>> > } >>> > >>> > void main() { >>> > println(treasure(Pirate.OTHER)); >>> > } >>> > >>> > So it is reasonable for a student to have made a relatively complex >>> program before having to get to that point, but I think you do need to >>> explain what exactly is going on with the anonymous main class when you >>> introduce multi file programs. >>> > >>> > As originally pitched, this transition would have just meant wrapping >>> the whole program in class Main {}, but now to make the transition from >>> > >>> > void main() { >>> > // Arbitrary code >>> > } >>> > >>> > to >>> > >>> > class Main { >>> > void main() { >>> > // Arbitrary code >>> > } >>> > } >>> > >>> > In a robust way, you need to add those imports to the top. You can >>> skip the final since the semantics of extension haven't been relevant yet. >>> > >>> > import static java.io.IO.*; >>> > >>> > import module java.base; >>> > >>> > class Main { >>> > void main() { >>> > // Arbitrary code >>> > } >>> > } >>> > >>> > My gripe is that the concepts introduced here - static, module, and * >>> imports - would have had no place to be introduced earlier. >>> > >>> > If you have folks write static methods on inner classes, the metaphor >>> of a "global field" that otherwise exists in the simple-main world goes >>> away. >>> > >>> > // For every program up until this, they could freely access this from >>> anywhere >>> > int count = 0; >>> > >>> > // And they could freely make an instance of any inner class >>> > class Other {} >>> > >>> > class Pos { >>> > static Pos of(int x, int y) { >>> > // So the rules they are used to don't apply anymore >>> > // and the explanation as to why really lies *after* they >>> understand >>> > // what an anonymous main class is and does >>> > // ... >>> > } >>> > } >>> > >>> > void main() { >>> > // ... >>> > } >>> > >>> > If you have folks *use* static methods that is fine - the hand-waving >>> of Math.max doesn't seem to trip anyone up - but I can't figure out how to >>> topologically sort topics such that import static makes any sense before >>> going through multi-file programs. >>> > >>> > I have a similar concern with import module, but that can be hand >>> waved as "gets you everything from this library" so I am less concerned. >>> Still don't fully understand the desire to have it be implicit, but less >>> concerned. >>> > >>> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new >>> import rules needed and now IO is available to all programs. >>> > >>> > void main() { >>> > IO.printLine("Hello, world"); >>> > } >>> > >>> > While this does introduce something not explained - static method >>> access - it feels easier to hand-wave away than import static java.io.IO.*; >>> would be. It calls the printLine method, it comes from IO. IO means >>> "Input/Output". That's a workable metaphor and can be glanced over in the >>> same way as Math.max >>> > >>> > The transition from here to classes can again be "it's the same as if >>> you had class Main {} around it" instead of "it's the same as if you had >>> class Main {} around it and these imports. A static import imports a static >>> method. A static method is a method attached to the class itself instead of >>> the instance.... etc." >>> > >>> > Also, IO. feels like a different kind of unexplained boilerplate than >>> public static void main(String[] args) {}. There is a lot of ground you >>> need to cover before access modifiers, static methods, or command line >>> arguments make any sort of sense. When you have someone write IO they are >>> in that moment producing output and will soon take input. >>> > >>> > What am I overlooking? >>> > >>> > >>> > [1]: https://openjdk.org/jeps/477 >>> > [2]: >>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >>> > [3] >>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Jun 2 02:57:29 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 1 Jun 2024 22:57:29 -0400 Subject: Question about Pattern-Matching for Switch Message-ID: Hello Amber Dev, Apologies for not having tested many features lately for this project (and others). Juggling insane workloads at work. Today is the first day I had a moment to breath in months, so I tried out some stuff, and I was confused. I have a Switch Expression where the input is a Sealed Type. For this particular Switch Expression, almost every branch of it is going to throw an Exception of some sort. I added all of the failure cases to the Switch Expression, pressed compile, and it threw the following compilation error. > InputFormat.java:161: error: switch expression does not have any result expressions > switch (next) Again, there is going to be one result expression, so I am not blocked by this compilation error. I was just really surprised to see an intentional road block there. Why is there a compilation error if all of the arms of a Switch Expression are just throwing Exceptions? Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Sun Jun 2 03:21:51 2024 From: ethan at mccue.dev (Ethan McCue) Date: Sat, 1 Jun 2024 23:21:51 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: I really should find a better example / adapt something in an existing curriculum - but: Mutation 1: Make b static int count = 0; class A { static void b() { count++; c(); new D(); } } void c() { println("" + count); } class D {} void main() { new A().b(); } https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=be416fe8e6df84c81c0f5526f0e28aef Mutation 2: Make c static int count = 0; class A { void b() { count++; c(); new D(); } } static void c() { println("" + count); } class D {} void main() { new A().b(); } https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=b71e9b94cff6099afb5153abffdbc3e9 Mutation 3: (for fun) Make A a record int count = 0; record A() { void b() { count++; c(); new D(); } } void c() { println("" + count); } class D {} void main() { new A().b(); } https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=cb5bf4664bf83ffc8ee4e5fe85abadb6 Maybe it's just a failure of imagination, but I don't know how to introduce static methods and fields into someone's arsenal without either * pulling the veil and explaining the anonymous class. This requires static imports. We've looped. * leaving them with guidance like "you can't make classes, records work though. I'll tell you later why. Just do math type stuff here." Then there is the confounding factor of *why* they would want a static method. In "real code" we use static methods and fields for * globals * "pure" logic * factory methods Before they move to another file, they already have globals and they can already put pure logic at the top level. Factory methods only make sense once you've introduced large program topics like visibility. I.E. you only need factory methods if 1. You want to hide a constructor class Point { private Point(int x, int y) {} static Point of(int x, int y) { return new Point(x, y); } } But this is a nuanced api topic and isn't even relevant when everything is a nestmate. Really a "large program" sort of choice. 2. You want to create through an interface interface LinkedList { int size(); static LinkedList empty() { return new EmptyList(); } static LinkedList notEmpty(int head, LinkedList tail) { } } class EmptyList implements LinkedList { @Override public int size() { return 0; } } But as you see from the code you need to have public to teach interfaces. You can gloss over it (my school did) but it's there. Even then "why not just new EmptyList()" has an answer really rooted in (again) large program concerns. You make code for potentially an external audience and need to give a "good" API. 3. You want otherwise incompatible overloads class Point { Point(int x, int y) {} static Point ofX(int x) { return new Point(x, 0); } static Point ofY(int y) { return new Point(0, y); } } Which does work a little. But it doesn't really give a good opener for static imports. The method names you'd give here are like ofX, fromX. You would want to use those qualified every time. You also activate the footguns I showed above. int pointsMade = 0; class Point { Point(int x, int y) {} static Point ofX(int x) { pointsMade++; return new Point(x, 0); } static Point ofY(int y) { pointsMade++; return new Point(0, y); } } -- > I would always start by taking one of their projects, ask them to tweak in a subtle, but difficult way, then let them run headfirst into the problem. Once they start to feel the strain, I would introduce them to the concepts of classes. I think this is what I am missing. Without going into large or multi-file program design there isn't much reason to use static anything. *In addition* to that there are mechanical difficulties that can only be explained properly once you reach a 2nd file. I.E. - "you were actually using a class until now. For true globals use static fields", "For methods you can call anywhere use static" are the explanations I want to give. Both of those explanations need multiple files already in place to make sense. Does that track? On Sat, Jun 1, 2024 at 10:47?PM David Alayachew wrote: > > Heh, we're both unpaid :P I tutor folks from my university and church, as > well as a couple of friends and family. Slowing down now though because of > workload at work. > > But back to the point. > > > I think the problem is that without static "global" > > fields are always accessible and all the nested classes > > in their program are instansable from anywhere. > > > > Once you have a static method, a method on a record, a > > method on an enum, or a static inner class this is no > > longer the case. > > Apologies, maybe I missed the memo. I have been out of touch with this > (and all other JEP's) because of my insane workload for the past couple of > months. > > Why would adding a static method change any of the logic? > > I took your example in Java 22, added a static method, and everything > worked dandy. > > Am I missing something? > > On Sat, Jun 1, 2024 at 9:28?PM Ethan McCue wrote: > >> I am also basically just a tutor, just unpaid. >> >> I think the problem is that without static "global" fields are always >> accessible and all the nested classes in their program are instansable from >> anywhere. >> >> Once you have a static method, a method on a record, a method on an enum, >> or a static inner class this is no longer the case. >> >> Trite example of the mechanics I'm referencing: >> >> int count = 0; >> >> class A { >> void b() { >> count++; >> c(); >> new D(); >> } >> } >> >> void c() { >> println("" + count); >> } >> >> class D {} >> >> void main() { >> new A().b(); >> } >> >> So when you show static methods, I think you need to understand why >> count++; and c(); and new D(); would not function. >> >> That requires knowing that you are actually in an anonymous class. That >> requires we get past import static java.io.IO. That's the loop. >> >> >> >> On Sat, Jun 1, 2024, 8:59 PM David Alayachew >> wrote: >> >>> Maybe I am missing something, but a static import seems like the natural >>> next step to teaching a student who made it that far. >>> >>> If a student feels the need to break out of the bounds of an implicitly >>> declared class, they must first understand what a class is going to save >>> them from. In this case, it is allowing code they right to be reused else >>> where. >>> >>> I am only a tutor, not a full-blown teacher, but when tutoring folks in >>> this exact subject, I would always start by taking one of their projects, >>> ask them to tweak in a subtle, but difficult way, then let them run >>> headfirst into the problem. Once they start to feel the strain, I would >>> introduce them to the concepts of classes. >>> >>> I would start by showing them how to create a simple pure function, >>> similar to java.lang.Math. From there, we would make a few pure functions >>> that are unrelated to their current task, and then have them get >>> comfortable with that concept. >>> >>> Then, we would make another pure function that is being done repeatedly >>> in their "JumboClass1" and "JumboClass2", and then have them get >>> comfortable using this helper method in both versions. >>> >>> In my mind, this is naturally where the student would discover that they >>> need to do a static import. In which case, they are in the middle of >>> understanding a class, they understand imports, and they understand static >>> methods fairly well. So, a static import has been well prepared for them. >>> >>> For me, this is a neat and clean introduction. Maybe I am missing >>> something? >>> >>> On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler >>> wrote: >>> >>>> Hi. >>>> >>>> Without getting into the merits of not implicitly importing anything, >>>> let me just point out that the following is a valid program under the >>>> current JEP: >>>> >>>> void main() { >>>> IO.println(?Hello, world!?); >>>> } >>>> >>>> As is this one: >>>> >>>> import module java.base; >>>> >>>> void main() { >>>> IO.println(?Hello, world!?); >>>> } >>>> >>>> In other words, the fact that there are implicit imports doesn?t mean >>>> that you can?t ignore them or add them explicitly. if you find teaching >>>> this to be easier, you can. So even if you don?t find implicit imports >>>> helpful, they may be helpful to other teachers, who may not want to start >>>> with some import incantation that necessarily implies some >>>> programming-in-the-large concept. >>>> >>>> But I think that your unease mostly stems from the extra magic that >>>> implicit classes enjoy, and which isn?t strictly necessitated by their >>>> primary quality of being implicitly declared classes, and that extra magic >>>> differentiates them from regular compilation units along some other axis; >>>> is that right? >>>> >>>> ? Ron >>>> >>>> >>>> >>>> > On 1 Jun 2024, at 23:14, Ethan McCue wrote: >>>> > >>>> > Hi all, >>>> > >>>> > I'm following the development of JEP 477[1] and I feel the need to >>>> question the impetus for the implicit static imports. >>>> > >>>> > As of now[2] any program like this >>>> > >>>> > void main() { >>>> > println("Hello, world"); >>>> > } >>>> > >>>> > Is equivalent to >>>> > >>>> > import static java.io.IO.print; >>>> > import static java.io.IO.println; >>>> > import static java.io.IO.writeln; >>>> > >>>> > import module java.base; >>>> > >>>> > final class Main { >>>> > void main() { >>>> > println("Hello, world"); >>>> > } >>>> > } >>>> > >>>> > Where all the methods in java.io.IO delegate to newly added >>>> equivalent methods in java.io.Console.[3] >>>> > >>>> > Aside from muddying that API up (now there is readln and readLine + >>>> println and printLine which...what) I'm still concerned on how those >>>> implicit imports will affect the transition to named classes. >>>> > >>>> > Assume we start a student out here >>>> > >>>> > void main() { >>>> > println("Hello, world"); >>>> > } >>>> > >>>> > You can get through conditionals, loops, variables, methods, and >>>> return types before touching classes or access specifiers. >>>> > >>>> > int compute() { >>>> > int total = 0; >>>> > for (int i = 0; i < 10; i++) { >>>> > total += i; >>>> > } >>>> > return total; >>>> > } >>>> > >>>> > void main() { >>>> > println("Hello: " + compute()); >>>> > } >>>> > >>>> > You can even talk about records and enums in a hand wavey way. Enums >>>> are "one of these options", Records are "if you want to return two things." >>>> > >>>> > enum Pirate { >>>> > BLACKBEARD, >>>> > OTHER >>>> > } >>>> > >>>> > record Pos(int x, int y) {} >>>> > >>>> > Pos treasure(Pirate pirate) { >>>> > switch (pirate) { >>>> > case BLACKBEARD -> >>>> > return new Pos(5, 5); >>>> > case OTHER -> >>>> > return new Pos(0, 0); >>>> > } >>>> > } >>>> > >>>> > void main() { >>>> > println(treasure(Pirate.OTHER)); >>>> > } >>>> > >>>> > So it is reasonable for a student to have made a relatively complex >>>> program before having to get to that point, but I think you do need to >>>> explain what exactly is going on with the anonymous main class when you >>>> introduce multi file programs. >>>> > >>>> > As originally pitched, this transition would have just meant wrapping >>>> the whole program in class Main {}, but now to make the transition from >>>> > >>>> > void main() { >>>> > // Arbitrary code >>>> > } >>>> > >>>> > to >>>> > >>>> > class Main { >>>> > void main() { >>>> > // Arbitrary code >>>> > } >>>> > } >>>> > >>>> > In a robust way, you need to add those imports to the top. You can >>>> skip the final since the semantics of extension haven't been relevant yet. >>>> > >>>> > import static java.io.IO.*; >>>> > >>>> > import module java.base; >>>> > >>>> > class Main { >>>> > void main() { >>>> > // Arbitrary code >>>> > } >>>> > } >>>> > >>>> > My gripe is that the concepts introduced here - static, module, and * >>>> imports - would have had no place to be introduced earlier. >>>> > >>>> > If you have folks write static methods on inner classes, the metaphor >>>> of a "global field" that otherwise exists in the simple-main world goes >>>> away. >>>> > >>>> > // For every program up until this, they could freely access this >>>> from anywhere >>>> > int count = 0; >>>> > >>>> > // And they could freely make an instance of any inner class >>>> > class Other {} >>>> > >>>> > class Pos { >>>> > static Pos of(int x, int y) { >>>> > // So the rules they are used to don't apply anymore >>>> > // and the explanation as to why really lies *after* they >>>> understand >>>> > // what an anonymous main class is and does >>>> > // ... >>>> > } >>>> > } >>>> > >>>> > void main() { >>>> > // ... >>>> > } >>>> > >>>> > If you have folks *use* static methods that is fine - the hand-waving >>>> of Math.max doesn't seem to trip anyone up - but I can't figure out how to >>>> topologically sort topics such that import static makes any sense before >>>> going through multi-file programs. >>>> > >>>> > I have a similar concern with import module, but that can be hand >>>> waved as "gets you everything from this library" so I am less concerned. >>>> Still don't fully understand the desire to have it be implicit, but less >>>> concerned. >>>> > >>>> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new >>>> import rules needed and now IO is available to all programs. >>>> > >>>> > void main() { >>>> > IO.printLine("Hello, world"); >>>> > } >>>> > >>>> > While this does introduce something not explained - static method >>>> access - it feels easier to hand-wave away than import static java.io.IO.*; >>>> would be. It calls the printLine method, it comes from IO. IO means >>>> "Input/Output". That's a workable metaphor and can be glanced over in the >>>> same way as Math.max >>>> > >>>> > The transition from here to classes can again be "it's the same as if >>>> you had class Main {} around it" instead of "it's the same as if you had >>>> class Main {} around it and these imports. A static import imports a static >>>> method. A static method is a method attached to the class itself instead of >>>> the instance.... etc." >>>> > >>>> > Also, IO. feels like a different kind of unexplained boilerplate than >>>> public static void main(String[] args) {}. There is a lot of ground you >>>> need to cover before access modifiers, static methods, or command line >>>> arguments make any sort of sense. When you have someone write IO they are >>>> in that moment producing output and will soon take input. >>>> > >>>> > What am I overlooking? >>>> > >>>> > >>>> > [1]: https://openjdk.org/jeps/477 >>>> > [2]: >>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >>>> > [3] >>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Jun 2 04:05:34 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 2 Jun 2024 00:05:34 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: Well, I definitely see the discrepancy now. Yes, this makes a lot of sense. Apologies for not recognizing your point earlier, I had assumed that you were talking about some fundamental execution change that the kid needs to be aware of. I didn't realize that you were talking about potholes that the kid might fall into. Long story short, you and I approach tutoring in WILDLY different ways. First and foremost, the concept of introducing instance state this early on is dangerous in my eyes. If you have a kid that can dance at that tempo, then you truly have a gifted kid. But from my 10+ years of tutoring, EVERY SINGLE TIME I try to introduce them to the idea of having state ANYWHERE except for inside of a method, things fall apart. It will be a long time before I ever try and get this kid to try and manage state at the instance level. I approach it very differently. I start off teaching these kids to approach problems with a functional mindset. Pure functions everywhere. This usually ends up creating a big, singular essay of a function, and once they start to feel the strain from that, I show them how to thread their state in through methods and return values. Anyways, back to my example from 2 emails ago. Their JumboClassA is very big and unwieldy, and they want to use parts of it for JumboClassB, and maybe other things I've introduced them to at that point. If I haven't already, I introduce them to the Math class, show them how it works, let them make a HelloWorld that does something meaningless, let them call it in their existing class, let them call it in another project, let them make a Math2, and then from there, they usually feel comfortable enough to try and extract a method from what they were actually doing into a pure, static function. I have only tutored one student using this implicit class feature so far, and we are nowhere near this point yet. But I expect that the only thing that will change before I get there is explaining to them how, in order to use their methods outside of the class, they need to give the class an explicit name. I foresee no pain points with that. After all, how can you reference a class that does not have an explicit name? And I expect them to understand the unidirectionality of that. A class without a name can use methods from a class WITH a name. But the reverse is false. I'm actually sorted of excited to spring that onto the kids and see how they react lol. Really, the only issue I ever run into is when kids ask me, what is static useful for anyways? They usually feel like they only need static or instance methods. I guess what I am saying is that, since I don't really depend on static or instance level state, I just don't have this problem. And by the time I do, they have gotten so used to doing functional, that any interaction with global state stands out as something to watch out for. From there, they understand how much of an ordeal it is to work with state, and exactly how dangerous and error-prone that it is that I can usually get them to turn their alertness up to 11 whenever the need arises. Let me first get your perspective though before I continue my point. What are your thoughts on this? And apologies for not responding to your points individually. Our assumptions are on opposite ends, so there is very little that I can directly respond to. On Sat, Jun 1, 2024 at 11:22?PM Ethan McCue wrote: > I really should find a better example / adapt something in an existing > curriculum - but: > > Mutation 1: Make b static > > int count = 0; > > class A { > static void b() { > count++; > c(); > new D(); > } > } > > void c() { > println("" + count); > } > > class D {} > > void main() { > new A().b(); > } > > > https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=be416fe8e6df84c81c0f5526f0e28aef > > Mutation 2: Make c static > > int count = 0; > > class A { > void b() { > count++; > c(); > new D(); > } > } > > static void c() { > println("" + count); > } > > class D {} > > void main() { > new A().b(); > } > > > https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=b71e9b94cff6099afb5153abffdbc3e9 > > Mutation 3: (for fun) Make A a record > > int count = 0; > > record A() { > void b() { > count++; > c(); > new D(); > } > } > > void c() { > println("" + count); > } > > class D {} > > void main() { > new A().b(); > } > > > https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=cb5bf4664bf83ffc8ee4e5fe85abadb6 > > Maybe it's just a failure of imagination, but I don't know how to > introduce static methods and fields into someone's arsenal without either > * pulling the veil and explaining the anonymous class. This requires > static imports. We've looped. > * leaving them with guidance like "you can't make classes, records work > though. I'll tell you later why. Just do math type stuff here." > > Then there is the confounding factor of *why* they would want a static > method. In "real code" we use static methods and fields for > > * globals > * "pure" logic > * factory methods > > Before they move to another file, they already have globals and they can > already put pure logic at the top level. Factory methods only make sense > once you've introduced large program topics like visibility. > > I.E. you only need factory methods if > > 1. You want to hide a constructor > > class Point { > private Point(int x, int y) {} > > static Point of(int x, int y) { > return new Point(x, y); > } > } > > But this is a nuanced api topic and isn't even relevant when everything is > a nestmate. Really a "large program" sort of choice. > > 2. You want to create through an interface > > interface LinkedList { > int size(); > > static LinkedList empty() { > return new EmptyList(); > } > > static LinkedList notEmpty(int head, LinkedList tail) { > > } > } > > class EmptyList implements LinkedList { > @Override > public int size() { > return 0; > } > } > > But as you see from the code you need to have public to teach interfaces. > You can gloss over it (my school did) but it's there. Even then "why not > just new EmptyList()" has an answer really rooted in (again) large program > concerns. You make code for potentially an external audience and need to > give a "good" API. > > 3. You want otherwise incompatible overloads > > class Point { > Point(int x, int y) {} > > static Point ofX(int x) { > return new Point(x, 0); > } > > static Point ofY(int y) { > return new Point(0, y); > } > } > > Which does work a little. But it doesn't really give a good opener for > static imports. The method names you'd give here are like ofX, fromX. You > would want to use those qualified every time. You also activate the > footguns I showed above. > > int pointsMade = 0; > > class Point { > Point(int x, int y) {} > > static Point ofX(int x) { > pointsMade++; > return new Point(x, 0); > } > > static Point ofY(int y) { > pointsMade++; > return new Point(0, y); > } > } > > -- > > > I would always start by taking one of their projects, ask them to tweak > in a subtle, but difficult way, then let them run headfirst into the > problem. Once they start to feel the strain, I would introduce them to the > concepts of classes. > > I think this is what I am missing. Without going into large or multi-file > program design there isn't much reason to use static anything. *In > addition* to that there are mechanical difficulties that can only be > explained properly once you reach a 2nd file. > > I.E. - "you were actually using a class until now. For true globals use > static fields", "For methods you can call anywhere use static" are the > explanations I want to give. Both of those explanations need multiple files > already in place to make sense. > > Does that track? > > > > On Sat, Jun 1, 2024 at 10:47?PM David Alayachew > wrote: > >> >> Heh, we're both unpaid :P I tutor folks from my university and church, as >> well as a couple of friends and family. Slowing down now though because of >> workload at work. >> >> But back to the point. >> >> > I think the problem is that without static "global" >> > fields are always accessible and all the nested classes >> > in their program are instansable from anywhere. >> > >> > Once you have a static method, a method on a record, a >> > method on an enum, or a static inner class this is no >> > longer the case. >> >> Apologies, maybe I missed the memo. I have been out of touch with this >> (and all other JEP's) because of my insane workload for the past couple of >> months. >> >> Why would adding a static method change any of the logic? >> >> I took your example in Java 22, added a static method, and everything >> worked dandy. >> >> Am I missing something? >> >> On Sat, Jun 1, 2024 at 9:28?PM Ethan McCue wrote: >> >>> I am also basically just a tutor, just unpaid. >>> >>> I think the problem is that without static "global" fields are always >>> accessible and all the nested classes in their program are instansable from >>> anywhere. >>> >>> Once you have a static method, a method on a record, a method on an >>> enum, or a static inner class this is no longer the case. >>> >>> Trite example of the mechanics I'm referencing: >>> >>> int count = 0; >>> >>> class A { >>> void b() { >>> count++; >>> c(); >>> new D(); >>> } >>> } >>> >>> void c() { >>> println("" + count); >>> } >>> >>> class D {} >>> >>> void main() { >>> new A().b(); >>> } >>> >>> So when you show static methods, I think you need to understand why >>> count++; and c(); and new D(); would not function. >>> >>> That requires knowing that you are actually in an anonymous class. That >>> requires we get past import static java.io.IO. That's the loop. >>> >>> >>> >>> On Sat, Jun 1, 2024, 8:59 PM David Alayachew >>> wrote: >>> >>>> Maybe I am missing something, but a static import seems like the >>>> natural next step to teaching a student who made it that far. >>>> >>>> If a student feels the need to break out of the bounds of an implicitly >>>> declared class, they must first understand what a class is going to save >>>> them from. In this case, it is allowing code they right to be reused else >>>> where. >>>> >>>> I am only a tutor, not a full-blown teacher, but when tutoring folks in >>>> this exact subject, I would always start by taking one of their projects, >>>> ask them to tweak in a subtle, but difficult way, then let them run >>>> headfirst into the problem. Once they start to feel the strain, I would >>>> introduce them to the concepts of classes. >>>> >>>> I would start by showing them how to create a simple pure function, >>>> similar to java.lang.Math. From there, we would make a few pure functions >>>> that are unrelated to their current task, and then have them get >>>> comfortable with that concept. >>>> >>>> Then, we would make another pure function that is being done repeatedly >>>> in their "JumboClass1" and "JumboClass2", and then have them get >>>> comfortable using this helper method in both versions. >>>> >>>> In my mind, this is naturally where the student would discover that >>>> they need to do a static import. In which case, they are in the middle of >>>> understanding a class, they understand imports, and they understand static >>>> methods fairly well. So, a static import has been well prepared for them. >>>> >>>> For me, this is a neat and clean introduction. Maybe I am missing >>>> something? >>>> >>>> On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler >>>> wrote: >>>> >>>>> Hi. >>>>> >>>>> Without getting into the merits of not implicitly importing anything, >>>>> let me just point out that the following is a valid program under the >>>>> current JEP: >>>>> >>>>> void main() { >>>>> IO.println(?Hello, world!?); >>>>> } >>>>> >>>>> As is this one: >>>>> >>>>> import module java.base; >>>>> >>>>> void main() { >>>>> IO.println(?Hello, world!?); >>>>> } >>>>> >>>>> In other words, the fact that there are implicit imports doesn?t mean >>>>> that you can?t ignore them or add them explicitly. if you find teaching >>>>> this to be easier, you can. So even if you don?t find implicit imports >>>>> helpful, they may be helpful to other teachers, who may not want to start >>>>> with some import incantation that necessarily implies some >>>>> programming-in-the-large concept. >>>>> >>>>> But I think that your unease mostly stems from the extra magic that >>>>> implicit classes enjoy, and which isn?t strictly necessitated by their >>>>> primary quality of being implicitly declared classes, and that extra magic >>>>> differentiates them from regular compilation units along some other axis; >>>>> is that right? >>>>> >>>>> ? Ron >>>>> >>>>> >>>>> >>>>> > On 1 Jun 2024, at 23:14, Ethan McCue wrote: >>>>> > >>>>> > Hi all, >>>>> > >>>>> > I'm following the development of JEP 477[1] and I feel the need to >>>>> question the impetus for the implicit static imports. >>>>> > >>>>> > As of now[2] any program like this >>>>> > >>>>> > void main() { >>>>> > println("Hello, world"); >>>>> > } >>>>> > >>>>> > Is equivalent to >>>>> > >>>>> > import static java.io.IO.print; >>>>> > import static java.io.IO.println; >>>>> > import static java.io.IO.writeln; >>>>> > >>>>> > import module java.base; >>>>> > >>>>> > final class Main { >>>>> > void main() { >>>>> > println("Hello, world"); >>>>> > } >>>>> > } >>>>> > >>>>> > Where all the methods in java.io.IO delegate to newly added >>>>> equivalent methods in java.io.Console.[3] >>>>> > >>>>> > Aside from muddying that API up (now there is readln and readLine + >>>>> println and printLine which...what) I'm still concerned on how those >>>>> implicit imports will affect the transition to named classes. >>>>> > >>>>> > Assume we start a student out here >>>>> > >>>>> > void main() { >>>>> > println("Hello, world"); >>>>> > } >>>>> > >>>>> > You can get through conditionals, loops, variables, methods, and >>>>> return types before touching classes or access specifiers. >>>>> > >>>>> > int compute() { >>>>> > int total = 0; >>>>> > for (int i = 0; i < 10; i++) { >>>>> > total += i; >>>>> > } >>>>> > return total; >>>>> > } >>>>> > >>>>> > void main() { >>>>> > println("Hello: " + compute()); >>>>> > } >>>>> > >>>>> > You can even talk about records and enums in a hand wavey way. Enums >>>>> are "one of these options", Records are "if you want to return two things." >>>>> > >>>>> > enum Pirate { >>>>> > BLACKBEARD, >>>>> > OTHER >>>>> > } >>>>> > >>>>> > record Pos(int x, int y) {} >>>>> > >>>>> > Pos treasure(Pirate pirate) { >>>>> > switch (pirate) { >>>>> > case BLACKBEARD -> >>>>> > return new Pos(5, 5); >>>>> > case OTHER -> >>>>> > return new Pos(0, 0); >>>>> > } >>>>> > } >>>>> > >>>>> > void main() { >>>>> > println(treasure(Pirate.OTHER)); >>>>> > } >>>>> > >>>>> > So it is reasonable for a student to have made a relatively complex >>>>> program before having to get to that point, but I think you do need to >>>>> explain what exactly is going on with the anonymous main class when you >>>>> introduce multi file programs. >>>>> > >>>>> > As originally pitched, this transition would have just meant >>>>> wrapping the whole program in class Main {}, but now to make the transition >>>>> from >>>>> > >>>>> > void main() { >>>>> > // Arbitrary code >>>>> > } >>>>> > >>>>> > to >>>>> > >>>>> > class Main { >>>>> > void main() { >>>>> > // Arbitrary code >>>>> > } >>>>> > } >>>>> > >>>>> > In a robust way, you need to add those imports to the top. You can >>>>> skip the final since the semantics of extension haven't been relevant yet. >>>>> > >>>>> > import static java.io.IO.*; >>>>> > >>>>> > import module java.base; >>>>> > >>>>> > class Main { >>>>> > void main() { >>>>> > // Arbitrary code >>>>> > } >>>>> > } >>>>> > >>>>> > My gripe is that the concepts introduced here - static, module, and >>>>> * imports - would have had no place to be introduced earlier. >>>>> > >>>>> > If you have folks write static methods on inner classes, the >>>>> metaphor of a "global field" that otherwise exists in the simple-main world >>>>> goes away. >>>>> > >>>>> > // For every program up until this, they could freely access this >>>>> from anywhere >>>>> > int count = 0; >>>>> > >>>>> > // And they could freely make an instance of any inner class >>>>> > class Other {} >>>>> > >>>>> > class Pos { >>>>> > static Pos of(int x, int y) { >>>>> > // So the rules they are used to don't apply anymore >>>>> > // and the explanation as to why really lies *after* they >>>>> understand >>>>> > // what an anonymous main class is and does >>>>> > // ... >>>>> > } >>>>> > } >>>>> > >>>>> > void main() { >>>>> > // ... >>>>> > } >>>>> > >>>>> > If you have folks *use* static methods that is fine - the >>>>> hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure >>>>> out how to topologically sort topics such that import static makes any >>>>> sense before going through multi-file programs. >>>>> > >>>>> > I have a similar concern with import module, but that can be hand >>>>> waved as "gets you everything from this library" so I am less concerned. >>>>> Still don't fully understand the desire to have it be implicit, but less >>>>> concerned. >>>>> > >>>>> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new >>>>> import rules needed and now IO is available to all programs. >>>>> > >>>>> > void main() { >>>>> > IO.printLine("Hello, world"); >>>>> > } >>>>> > >>>>> > While this does introduce something not explained - static method >>>>> access - it feels easier to hand-wave away than import static java.io.IO.*; >>>>> would be. It calls the printLine method, it comes from IO. IO means >>>>> "Input/Output". That's a workable metaphor and can be glanced over in the >>>>> same way as Math.max >>>>> > >>>>> > The transition from here to classes can again be "it's the same as >>>>> if you had class Main {} around it" instead of "it's the same as if you had >>>>> class Main {} around it and these imports. A static import imports a static >>>>> method. A static method is a method attached to the class itself instead of >>>>> the instance.... etc." >>>>> > >>>>> > Also, IO. feels like a different kind of unexplained boilerplate >>>>> than public static void main(String[] args) {}. There is a lot of ground >>>>> you need to cover before access modifiers, static methods, or command line >>>>> arguments make any sort of sense. When you have someone write IO they are >>>>> in that moment producing output and will soon take input. >>>>> > >>>>> > What am I overlooking? >>>>> > >>>>> > >>>>> > [1]: https://openjdk.org/jeps/477 >>>>> > [2]: >>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >>>>> > [3] >>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Sun Jun 2 07:57:05 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Sun, 2 Jun 2024 10:57:05 +0300 Subject: Question about Pattern-Matching for Switch In-Reply-To: References: Message-ID: Hi David! I`ve looked into compiler sources a bit, and this error is reported in Attr class when attributing switch expressions.Basically, compiler does not know which type to assign to switch expression, as there is not case return types, you could think of it as switch expression of "void" type, so it cant be nor assigned nor returned, so compiler treats it like an error. Its not a pattern matching thing, just general switch behaviour On Sun, Jun 2, 2024 at 5:58?AM David Alayachew wrote: > Hello Amber Dev, > > Apologies for not having tested many features lately for this project (and > others). Juggling insane workloads at work. > > Today is the first day I had a moment to breath in months, so I tried out > some stuff, and I was confused. > > I have a Switch Expression where the input is a Sealed Type. For this > particular Switch Expression, almost every branch of it is going to throw > an Exception of some sort. I added all of the failure cases to the Switch > Expression, pressed compile, and it threw the following compilation error. > > > InputFormat.java:161: error: switch expression does not have any result > expressions > > switch (next) > > Again, there is going to be one result expression, so I am not blocked by > this compilation error. > > I was just really surprised to see an intentional road block there. > > Why is there a compilation error if all of the arms of a Switch Expression > are just throwing Exceptions? > > Thank you for your time and help! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Jun 2 08:09:46 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 2 Jun 2024 04:09:46 -0400 Subject: Question about Pattern-Matching for Switch In-Reply-To: References: Message-ID: Makes sense, ty vm. If that is the justification, I am ok with it. I would prefer a more clear error though. On Sun, Jun 2, 2024 at 3:57?AM Olexandr Rotan wrote: > Hi David! I`ve looked into compiler sources a bit, and this error is > reported in Attr class when attributing switch expressions.Basically, > compiler does not know which type to assign to switch expression, as there > is not case return types, you could think of it as switch expression of > "void" type, so it cant be nor assigned nor returned, so compiler treats it > like an error. Its not a pattern matching thing, just general > switch behaviour > > On Sun, Jun 2, 2024 at 5:58?AM David Alayachew > wrote: > >> Hello Amber Dev, >> >> Apologies for not having tested many features lately for this project >> (and others). Juggling insane workloads at work. >> >> Today is the first day I had a moment to breath in months, so I tried out >> some stuff, and I was confused. >> >> I have a Switch Expression where the input is a Sealed Type. For this >> particular Switch Expression, almost every branch of it is going to throw >> an Exception of some sort. I added all of the failure cases to the Switch >> Expression, pressed compile, and it threw the following compilation error. >> >> > InputFormat.java:161: error: switch expression does not have any result >> expressions >> > switch (next) >> >> Again, there is going to be one result expression, so I am not blocked by >> this compilation error. >> >> I was just really surprised to see an intentional road block there. >> >> Why is there a compilation error if all of the arms of a Switch >> Expression are just throwing Exceptions? >> >> Thank you for your time and help! >> David Alayachew >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Sun Jun 2 19:36:07 2024 From: ethan at mccue.dev (Ethan McCue) Date: Sun, 2 Jun 2024 15:36:07 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: I'm pondering a bit so this isn't too deep of a response > Their JumboClassA is very big and unwieldy, and they want to use parts of it for JumboClassB, and maybe other things I've introduced them to at that point. If I haven't already, I introduce them to the Math class, show them how it works, let them make a HelloWorld that does something meaningless, let them call it in their existing class, let them call it in another project, let them make a Math2, and then from there, they usually feel comfortable enough to try and extract a method from what they were actually doing into a pure, static function. Are you saying that you only have them write static methods to start with? Like class JumboClassA { static int code() { return JumboClassB.compute() + 1; } } class JumboClassB { static int compute() { return 0; } } If so, do you oft get around to instances and instance state? > Long story short, you and I approach tutoring in WILDLY different ways. Also in different contexts. I spend most of my time in one of a few discord channels answering questions and that sometimes leads to people who I tutor over a longer period of time. We mostly get people who are either struggling with an existing curriculum and try to do course correction (I started making a shortlist of the bad college curriculums we are exposed to). So the things we notice are that current curriculums either * Have ordering issues within them. Explaining JPA before SQL, Abstract classes before interfaces, Large program design before small, etc. * Have no clear next steps. Most intro courses, after taking a deep sigh of relief for having gotten a successful for loop out of a teenager, just sorta end. So we have very few Java courses (that we know of) to point people to that actually explain the topics they introduce in the order they will be encountered. (See https://dev.java/learn/ , https://www.baeldung.com/java-tutorial, https://www.geeksforgeeks.org/java/ for examples of terrible tutorials) And after we give them something like https://java-programming.mooc.fi/ and they finish we have nowhere to go from there. That's part of why I think topic order matters so much. > I have only tutored one student using this implicit class feature so far, and we are nowhere near this point yet. Take notes on how that progresses + any differences from your previous approach. > I guess what I am saying is that, since I don't really depend on static or instance level state, I just don't have this problem. And by the time I do, they have gotten so used to doing functional, that any interaction with global state stands out as something to watch out for. I went to Northeastern and their intro curriculum starts you with Racket doing really rigid comments that drills in sum types + recursion. Then they introduce Java without mutation at all for 80% of the 2nd semester. You only get mutation at the end and at that point your brain was broken from all the purity you forget how. So I get it. But eventually those concepts need to be introduced. On Sun, Jun 2, 2024 at 12:06?AM David Alayachew wrote: > Well, I definitely see the discrepancy now. Yes, this makes a lot of sense. > > Apologies for not recognizing your point earlier, I had assumed that you > were talking about some fundamental execution change that the kid needs to > be aware of. I didn't realize that you were talking about potholes that the > kid might fall into. > > Long story short, you and I approach tutoring in WILDLY different ways. > > First and foremost, the concept of introducing instance state this early > on is dangerous in my eyes. If you have a kid that can dance at that tempo, > then you truly have a gifted kid. But from my 10+ years of tutoring, EVERY > SINGLE TIME I try to introduce them to the idea of having state ANYWHERE > except for inside of a method, things fall apart. It will be a long time > before I ever try and get this kid to try and manage state at the instance > level. > > I approach it very differently. > > I start off teaching these kids to approach problems with a functional > mindset. Pure functions everywhere. This usually ends up creating a big, > singular essay of a function, and once they start to feel the strain from > that, I show them how to thread their state in through methods and return > values. > > Anyways, back to my example from 2 emails ago. > > Their JumboClassA is very big and unwieldy, and they want to use parts of > it for JumboClassB, and maybe other things I've introduced them to at that > point. If I haven't already, I introduce them to the Math class, show them > how it works, let them make a HelloWorld that does something meaningless, > let them call it in their existing class, let them call it in another > project, let them make a Math2, and then from there, they usually feel > comfortable enough to try and extract a method from what they were actually > doing into a pure, static function. > > I have only tutored one student using this implicit class feature so far, > and we are nowhere near this point yet. But I expect that the only thing > that will change before I get there is explaining to them how, in order to > use their methods outside of the class, they need to give the class an > explicit name. I foresee no pain points with that. After all, how can you > reference a class that does not have an explicit name? And I expect them to > understand the unidirectionality of that. A class without a name can use > methods from a class WITH a name. But the reverse is false. I'm actually > sorted of excited to spring that onto the kids and see how they react lol. > > Really, the only issue I ever run into is when kids ask me, what is static > useful for anyways? They usually feel like they only need static or > instance methods. > > I guess what I am saying is that, since I don't really depend on static or > instance level state, I just don't have this problem. And by the time I do, > they have gotten so used to doing functional, that any interaction with > global state stands out as something to watch out for. From there, they > understand how much of an ordeal it is to work with state, and exactly how > dangerous and error-prone that it is that I can usually get them to turn > their alertness up to 11 whenever the need arises. > > Let me first get your perspective though before I continue my point. What > are your thoughts on this? > > And apologies for not responding to your points individually. Our > assumptions are on opposite ends, so there is very little that I can > directly respond to. > > On Sat, Jun 1, 2024 at 11:22?PM Ethan McCue wrote: > >> I really should find a better example / adapt something in an existing >> curriculum - but: >> >> Mutation 1: Make b static >> >> int count = 0; >> >> class A { >> static void b() { >> count++; >> c(); >> new D(); >> } >> } >> >> void c() { >> println("" + count); >> } >> >> class D {} >> >> void main() { >> new A().b(); >> } >> >> >> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=be416fe8e6df84c81c0f5526f0e28aef >> >> Mutation 2: Make c static >> >> int count = 0; >> >> class A { >> void b() { >> count++; >> c(); >> new D(); >> } >> } >> >> static void c() { >> println("" + count); >> } >> >> class D {} >> >> void main() { >> new A().b(); >> } >> >> >> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=b71e9b94cff6099afb5153abffdbc3e9 >> >> Mutation 3: (for fun) Make A a record >> >> int count = 0; >> >> record A() { >> void b() { >> count++; >> c(); >> new D(); >> } >> } >> >> void c() { >> println("" + count); >> } >> >> class D {} >> >> void main() { >> new A().b(); >> } >> >> >> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=cb5bf4664bf83ffc8ee4e5fe85abadb6 >> >> Maybe it's just a failure of imagination, but I don't know how to >> introduce static methods and fields into someone's arsenal without either >> * pulling the veil and explaining the anonymous class. This requires >> static imports. We've looped. >> * leaving them with guidance like "you can't make classes, records work >> though. I'll tell you later why. Just do math type stuff here." >> >> Then there is the confounding factor of *why* they would want a static >> method. In "real code" we use static methods and fields for >> >> * globals >> * "pure" logic >> * factory methods >> >> Before they move to another file, they already have globals and they can >> already put pure logic at the top level. Factory methods only make sense >> once you've introduced large program topics like visibility. >> >> I.E. you only need factory methods if >> >> 1. You want to hide a constructor >> >> class Point { >> private Point(int x, int y) {} >> >> static Point of(int x, int y) { >> return new Point(x, y); >> } >> } >> >> But this is a nuanced api topic and isn't even relevant when everything >> is a nestmate. Really a "large program" sort of choice. >> >> 2. You want to create through an interface >> >> interface LinkedList { >> int size(); >> >> static LinkedList empty() { >> return new EmptyList(); >> } >> >> static LinkedList notEmpty(int head, LinkedList tail) { >> >> } >> } >> >> class EmptyList implements LinkedList { >> @Override >> public int size() { >> return 0; >> } >> } >> >> But as you see from the code you need to have public to teach >> interfaces. You can gloss over it (my school did) but it's there. Even then >> "why not just new EmptyList()" has an answer really rooted in (again) large >> program concerns. You make code for potentially an external audience and >> need to give a "good" API. >> >> 3. You want otherwise incompatible overloads >> >> class Point { >> Point(int x, int y) {} >> >> static Point ofX(int x) { >> return new Point(x, 0); >> } >> >> static Point ofY(int y) { >> return new Point(0, y); >> } >> } >> >> Which does work a little. But it doesn't really give a good opener for >> static imports. The method names you'd give here are like ofX, fromX. You >> would want to use those qualified every time. You also activate the >> footguns I showed above. >> >> int pointsMade = 0; >> >> class Point { >> Point(int x, int y) {} >> >> static Point ofX(int x) { >> pointsMade++; >> return new Point(x, 0); >> } >> >> static Point ofY(int y) { >> pointsMade++; >> return new Point(0, y); >> } >> } >> >> -- >> >> > I would always start by taking one of their projects, ask them to >> tweak in a subtle, but difficult way, then let them run headfirst into the >> problem. Once they start to feel the strain, I would introduce them to the >> concepts of classes. >> >> I think this is what I am missing. Without going into large or multi-file >> program design there isn't much reason to use static anything. *In >> addition* to that there are mechanical difficulties that can only be >> explained properly once you reach a 2nd file. >> >> I.E. - "you were actually using a class until now. For true globals use >> static fields", "For methods you can call anywhere use static" are the >> explanations I want to give. Both of those explanations need multiple files >> already in place to make sense. >> >> Does that track? >> >> >> >> On Sat, Jun 1, 2024 at 10:47?PM David Alayachew >> wrote: >> >>> >>> Heh, we're both unpaid :P I tutor folks from my university and church, >>> as well as a couple of friends and family. Slowing down now though because >>> of workload at work. >>> >>> But back to the point. >>> >>> > I think the problem is that without static "global" >>> > fields are always accessible and all the nested classes >>> > in their program are instansable from anywhere. >>> > >>> > Once you have a static method, a method on a record, a >>> > method on an enum, or a static inner class this is no >>> > longer the case. >>> >>> Apologies, maybe I missed the memo. I have been out of touch with this >>> (and all other JEP's) because of my insane workload for the past couple of >>> months. >>> >>> Why would adding a static method change any of the logic? >>> >>> I took your example in Java 22, added a static method, and everything >>> worked dandy. >>> >>> Am I missing something? >>> >>> On Sat, Jun 1, 2024 at 9:28?PM Ethan McCue wrote: >>> >>>> I am also basically just a tutor, just unpaid. >>>> >>>> I think the problem is that without static "global" fields are always >>>> accessible and all the nested classes in their program are instansable from >>>> anywhere. >>>> >>>> Once you have a static method, a method on a record, a method on an >>>> enum, or a static inner class this is no longer the case. >>>> >>>> Trite example of the mechanics I'm referencing: >>>> >>>> int count = 0; >>>> >>>> class A { >>>> void b() { >>>> count++; >>>> c(); >>>> new D(); >>>> } >>>> } >>>> >>>> void c() { >>>> println("" + count); >>>> } >>>> >>>> class D {} >>>> >>>> void main() { >>>> new A().b(); >>>> } >>>> >>>> So when you show static methods, I think you need to understand why >>>> count++; and c(); and new D(); would not function. >>>> >>>> That requires knowing that you are actually in an anonymous class. That >>>> requires we get past import static java.io.IO. That's the loop. >>>> >>>> >>>> >>>> On Sat, Jun 1, 2024, 8:59 PM David Alayachew >>>> wrote: >>>> >>>>> Maybe I am missing something, but a static import seems like the >>>>> natural next step to teaching a student who made it that far. >>>>> >>>>> If a student feels the need to break out of the bounds of an >>>>> implicitly declared class, they must first understand what a class is going >>>>> to save them from. In this case, it is allowing code they right to be >>>>> reused else where. >>>>> >>>>> I am only a tutor, not a full-blown teacher, but when tutoring folks >>>>> in this exact subject, I would always start by taking one of their >>>>> projects, ask them to tweak in a subtle, but difficult way, then let them >>>>> run headfirst into the problem. Once they start to feel the strain, I would >>>>> introduce them to the concepts of classes. >>>>> >>>>> I would start by showing them how to create a simple pure function, >>>>> similar to java.lang.Math. From there, we would make a few pure functions >>>>> that are unrelated to their current task, and then have them get >>>>> comfortable with that concept. >>>>> >>>>> Then, we would make another pure function that is being done >>>>> repeatedly in their "JumboClass1" and "JumboClass2", and then have them get >>>>> comfortable using this helper method in both versions. >>>>> >>>>> In my mind, this is naturally where the student would discover that >>>>> they need to do a static import. In which case, they are in the middle of >>>>> understanding a class, they understand imports, and they understand static >>>>> methods fairly well. So, a static import has been well prepared for them. >>>>> >>>>> For me, this is a neat and clean introduction. Maybe I am missing >>>>> something? >>>>> >>>>> On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler >>>>> wrote: >>>>> >>>>>> Hi. >>>>>> >>>>>> Without getting into the merits of not implicitly importing anything, >>>>>> let me just point out that the following is a valid program under the >>>>>> current JEP: >>>>>> >>>>>> void main() { >>>>>> IO.println(?Hello, world!?); >>>>>> } >>>>>> >>>>>> As is this one: >>>>>> >>>>>> import module java.base; >>>>>> >>>>>> void main() { >>>>>> IO.println(?Hello, world!?); >>>>>> } >>>>>> >>>>>> In other words, the fact that there are implicit imports doesn?t mean >>>>>> that you can?t ignore them or add them explicitly. if you find teaching >>>>>> this to be easier, you can. So even if you don?t find implicit imports >>>>>> helpful, they may be helpful to other teachers, who may not want to start >>>>>> with some import incantation that necessarily implies some >>>>>> programming-in-the-large concept. >>>>>> >>>>>> But I think that your unease mostly stems from the extra magic that >>>>>> implicit classes enjoy, and which isn?t strictly necessitated by their >>>>>> primary quality of being implicitly declared classes, and that extra magic >>>>>> differentiates them from regular compilation units along some other axis; >>>>>> is that right? >>>>>> >>>>>> ? Ron >>>>>> >>>>>> >>>>>> >>>>>> > On 1 Jun 2024, at 23:14, Ethan McCue >>>>>> wrote: >>>>>> > >>>>>> > Hi all, >>>>>> > >>>>>> > I'm following the development of JEP 477[1] and I feel the need to >>>>>> question the impetus for the implicit static imports. >>>>>> > >>>>>> > As of now[2] any program like this >>>>>> > >>>>>> > void main() { >>>>>> > println("Hello, world"); >>>>>> > } >>>>>> > >>>>>> > Is equivalent to >>>>>> > >>>>>> > import static java.io.IO.print; >>>>>> > import static java.io.IO.println; >>>>>> > import static java.io.IO.writeln; >>>>>> > >>>>>> > import module java.base; >>>>>> > >>>>>> > final class Main { >>>>>> > void main() { >>>>>> > println("Hello, world"); >>>>>> > } >>>>>> > } >>>>>> > >>>>>> > Where all the methods in java.io.IO delegate to newly added >>>>>> equivalent methods in java.io.Console.[3] >>>>>> > >>>>>> > Aside from muddying that API up (now there is readln and readLine + >>>>>> println and printLine which...what) I'm still concerned on how those >>>>>> implicit imports will affect the transition to named classes. >>>>>> > >>>>>> > Assume we start a student out here >>>>>> > >>>>>> > void main() { >>>>>> > println("Hello, world"); >>>>>> > } >>>>>> > >>>>>> > You can get through conditionals, loops, variables, methods, and >>>>>> return types before touching classes or access specifiers. >>>>>> > >>>>>> > int compute() { >>>>>> > int total = 0; >>>>>> > for (int i = 0; i < 10; i++) { >>>>>> > total += i; >>>>>> > } >>>>>> > return total; >>>>>> > } >>>>>> > >>>>>> > void main() { >>>>>> > println("Hello: " + compute()); >>>>>> > } >>>>>> > >>>>>> > You can even talk about records and enums in a hand wavey way. >>>>>> Enums are "one of these options", Records are "if you want to return two >>>>>> things." >>>>>> > >>>>>> > enum Pirate { >>>>>> > BLACKBEARD, >>>>>> > OTHER >>>>>> > } >>>>>> > >>>>>> > record Pos(int x, int y) {} >>>>>> > >>>>>> > Pos treasure(Pirate pirate) { >>>>>> > switch (pirate) { >>>>>> > case BLACKBEARD -> >>>>>> > return new Pos(5, 5); >>>>>> > case OTHER -> >>>>>> > return new Pos(0, 0); >>>>>> > } >>>>>> > } >>>>>> > >>>>>> > void main() { >>>>>> > println(treasure(Pirate.OTHER)); >>>>>> > } >>>>>> > >>>>>> > So it is reasonable for a student to have made a relatively complex >>>>>> program before having to get to that point, but I think you do need to >>>>>> explain what exactly is going on with the anonymous main class when you >>>>>> introduce multi file programs. >>>>>> > >>>>>> > As originally pitched, this transition would have just meant >>>>>> wrapping the whole program in class Main {}, but now to make the transition >>>>>> from >>>>>> > >>>>>> > void main() { >>>>>> > // Arbitrary code >>>>>> > } >>>>>> > >>>>>> > to >>>>>> > >>>>>> > class Main { >>>>>> > void main() { >>>>>> > // Arbitrary code >>>>>> > } >>>>>> > } >>>>>> > >>>>>> > In a robust way, you need to add those imports to the top. You can >>>>>> skip the final since the semantics of extension haven't been relevant yet. >>>>>> > >>>>>> > import static java.io.IO.*; >>>>>> > >>>>>> > import module java.base; >>>>>> > >>>>>> > class Main { >>>>>> > void main() { >>>>>> > // Arbitrary code >>>>>> > } >>>>>> > } >>>>>> > >>>>>> > My gripe is that the concepts introduced here - static, module, and >>>>>> * imports - would have had no place to be introduced earlier. >>>>>> > >>>>>> > If you have folks write static methods on inner classes, the >>>>>> metaphor of a "global field" that otherwise exists in the simple-main world >>>>>> goes away. >>>>>> > >>>>>> > // For every program up until this, they could freely access this >>>>>> from anywhere >>>>>> > int count = 0; >>>>>> > >>>>>> > // And they could freely make an instance of any inner class >>>>>> > class Other {} >>>>>> > >>>>>> > class Pos { >>>>>> > static Pos of(int x, int y) { >>>>>> > // So the rules they are used to don't apply anymore >>>>>> > // and the explanation as to why really lies *after* they >>>>>> understand >>>>>> > // what an anonymous main class is and does >>>>>> > // ... >>>>>> > } >>>>>> > } >>>>>> > >>>>>> > void main() { >>>>>> > // ... >>>>>> > } >>>>>> > >>>>>> > If you have folks *use* static methods that is fine - the >>>>>> hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure >>>>>> out how to topologically sort topics such that import static makes any >>>>>> sense before going through multi-file programs. >>>>>> > >>>>>> > I have a similar concern with import module, but that can be hand >>>>>> waved as "gets you everything from this library" so I am less concerned. >>>>>> Still don't fully understand the desire to have it be implicit, but less >>>>>> concerned. >>>>>> > >>>>>> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new >>>>>> import rules needed and now IO is available to all programs. >>>>>> > >>>>>> > void main() { >>>>>> > IO.printLine("Hello, world"); >>>>>> > } >>>>>> > >>>>>> > While this does introduce something not explained - static method >>>>>> access - it feels easier to hand-wave away than import static java.io.IO.*; >>>>>> would be. It calls the printLine method, it comes from IO. IO means >>>>>> "Input/Output". That's a workable metaphor and can be glanced over in the >>>>>> same way as Math.max >>>>>> > >>>>>> > The transition from here to classes can again be "it's the same as >>>>>> if you had class Main {} around it" instead of "it's the same as if you had >>>>>> class Main {} around it and these imports. A static import imports a static >>>>>> method. A static method is a method attached to the class itself instead of >>>>>> the instance.... etc." >>>>>> > >>>>>> > Also, IO. feels like a different kind of unexplained boilerplate >>>>>> than public static void main(String[] args) {}. There is a lot of ground >>>>>> you need to cover before access modifiers, static methods, or command line >>>>>> arguments make any sort of sense. When you have someone write IO they are >>>>>> in that moment producing output and will soon take input. >>>>>> > >>>>>> > What am I overlooking? >>>>>> > >>>>>> > >>>>>> > [1]: https://openjdk.org/jeps/477 >>>>>> > [2]: >>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >>>>>> > [3] >>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >>>>>> >>>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Jun 2 21:05:09 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 2 Jun 2024 17:05:09 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: Sometimes, I don't get the choice to avoid state. But when the opportunity arises/the assignment doesn't require it, I will even encourage the student to undo some work TO AVOID using state. So no, if I get the chance, I avoid it every chance I get. As for static methods, I don't usually get to choose for them. The choice has usually been made, and there's a cost to undoing too much of their work. That said, most of what we work with is instance methods with as little state as possible. But to be clear, the most interaction they have with INSTANCES is calling it in their main method. Again, the goal is to get them using functional for as long as possible. Sad to see dev.java on the list, but I have to agree with you. Never considered it to be a beginner resource though. And as for being forced to eventually introduce state, you'd be shocked just how far we can go with it lol. I can usually get them to GUI's before I get forced into it. And even then, I don't let them introduce any state of their own, just modifying a JFrame and stuff along that order, which is incredibly easy to maintain for the kids. On Sun, Jun 2, 2024 at 3:36?PM Ethan McCue wrote: > I'm pondering a bit so this isn't too deep of a response > > > Their JumboClassA is very big and unwieldy, and they want to use parts > of it for JumboClassB, and maybe other things I've introduced them to at > that point. If I haven't already, I introduce them to the Math class, show > them how it works, let them make a HelloWorld that does something > meaningless, let them call it in their existing class, let them call it in > another project, let them make a Math2, and then from there, they usually > feel comfortable enough to try and extract a method from what they were > actually doing into a pure, static function. > > Are you saying that you only have them write static methods to start with? > Like > > class JumboClassA { > static int code() { > return JumboClassB.compute() + 1; > } > } > > class JumboClassB { > static int compute() { > return 0; > } > } > > If so, do you oft get around to instances and instance state? > > > Long story short, you and I approach tutoring in WILDLY different ways. > > Also in different contexts. I spend most of my time in one of a few > discord channels answering questions and that sometimes leads to people who > I tutor over a longer period of time. We mostly get people who are either > struggling with an existing curriculum and try to do course correction (I > started making a shortlist of the bad college curriculums we are exposed > to). > > So the things we notice are that current curriculums either > * Have ordering issues within them. Explaining JPA before SQL, Abstract > classes before interfaces, Large program design before small, etc. > * Have no clear next steps. Most intro courses, after taking a deep sigh > of relief for having gotten a successful for loop out of a teenager, just > sorta end. > > So we have very few Java courses (that we know of) to point people to that > actually explain the topics they introduce in the order they will be > encountered. (See https://dev.java/learn/ , > https://www.baeldung.com/java-tutorial, > https://www.geeksforgeeks.org/java/ for examples of terrible tutorials) > > And after we give them something like https://java-programming.mooc.fi/ > and they finish we have nowhere to go from there. That's part of why I > think topic order matters so much. > > > I have only tutored one student using this implicit class feature so > far, and we are nowhere near this point yet. > > Take notes on how that progresses + any differences from your previous > approach. > > > I guess what I am saying is that, since I don't really depend on static > or instance level state, I just don't have this problem. And by the time I > do, they have gotten so used to doing functional, that any interaction with > global state stands out as something to watch out for. > > I went to Northeastern and their intro curriculum starts you with Racket > doing really rigid comments that drills in sum types + recursion. Then they > introduce Java without mutation at all for 80% of the 2nd semester. You > only get mutation at the end and at that point your brain was broken from > all the purity you forget how. > > So I get it. But eventually those concepts need to be introduced. > > > On Sun, Jun 2, 2024 at 12:06?AM David Alayachew > wrote: > >> Well, I definitely see the discrepancy now. Yes, this makes a lot of >> sense. >> >> Apologies for not recognizing your point earlier, I had assumed that you >> were talking about some fundamental execution change that the kid needs to >> be aware of. I didn't realize that you were talking about potholes that the >> kid might fall into. >> >> Long story short, you and I approach tutoring in WILDLY different ways. >> >> First and foremost, the concept of introducing instance state this early >> on is dangerous in my eyes. If you have a kid that can dance at that tempo, >> then you truly have a gifted kid. But from my 10+ years of tutoring, EVERY >> SINGLE TIME I try to introduce them to the idea of having state ANYWHERE >> except for inside of a method, things fall apart. It will be a long time >> before I ever try and get this kid to try and manage state at the instance >> level. >> >> I approach it very differently. >> >> I start off teaching these kids to approach problems with a functional >> mindset. Pure functions everywhere. This usually ends up creating a big, >> singular essay of a function, and once they start to feel the strain from >> that, I show them how to thread their state in through methods and return >> values. >> >> Anyways, back to my example from 2 emails ago. >> >> Their JumboClassA is very big and unwieldy, and they want to use parts of >> it for JumboClassB, and maybe other things I've introduced them to at that >> point. If I haven't already, I introduce them to the Math class, show them >> how it works, let them make a HelloWorld that does something meaningless, >> let them call it in their existing class, let them call it in another >> project, let them make a Math2, and then from there, they usually feel >> comfortable enough to try and extract a method from what they were actually >> doing into a pure, static function. >> >> I have only tutored one student using this implicit class feature so far, >> and we are nowhere near this point yet. But I expect that the only thing >> that will change before I get there is explaining to them how, in order to >> use their methods outside of the class, they need to give the class an >> explicit name. I foresee no pain points with that. After all, how can you >> reference a class that does not have an explicit name? And I expect them to >> understand the unidirectionality of that. A class without a name can use >> methods from a class WITH a name. But the reverse is false. I'm actually >> sorted of excited to spring that onto the kids and see how they react lol. >> >> Really, the only issue I ever run into is when kids ask me, what is >> static useful for anyways? They usually feel like they only need static or >> instance methods. >> >> I guess what I am saying is that, since I don't really depend on static >> or instance level state, I just don't have this problem. And by the time I >> do, they have gotten so used to doing functional, that any interaction with >> global state stands out as something to watch out for. From there, they >> understand how much of an ordeal it is to work with state, and exactly how >> dangerous and error-prone that it is that I can usually get them to turn >> their alertness up to 11 whenever the need arises. >> >> Let me first get your perspective though before I continue my point. What >> are your thoughts on this? >> >> And apologies for not responding to your points individually. Our >> assumptions are on opposite ends, so there is very little that I can >> directly respond to. >> >> On Sat, Jun 1, 2024 at 11:22?PM Ethan McCue wrote: >> >>> I really should find a better example / adapt something in an existing >>> curriculum - but: >>> >>> Mutation 1: Make b static >>> >>> int count = 0; >>> >>> class A { >>> static void b() { >>> count++; >>> c(); >>> new D(); >>> } >>> } >>> >>> void c() { >>> println("" + count); >>> } >>> >>> class D {} >>> >>> void main() { >>> new A().b(); >>> } >>> >>> >>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=be416fe8e6df84c81c0f5526f0e28aef >>> >>> Mutation 2: Make c static >>> >>> int count = 0; >>> >>> class A { >>> void b() { >>> count++; >>> c(); >>> new D(); >>> } >>> } >>> >>> static void c() { >>> println("" + count); >>> } >>> >>> class D {} >>> >>> void main() { >>> new A().b(); >>> } >>> >>> >>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=b71e9b94cff6099afb5153abffdbc3e9 >>> >>> Mutation 3: (for fun) Make A a record >>> >>> int count = 0; >>> >>> record A() { >>> void b() { >>> count++; >>> c(); >>> new D(); >>> } >>> } >>> >>> void c() { >>> println("" + count); >>> } >>> >>> class D {} >>> >>> void main() { >>> new A().b(); >>> } >>> >>> >>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=cb5bf4664bf83ffc8ee4e5fe85abadb6 >>> >>> Maybe it's just a failure of imagination, but I don't know how to >>> introduce static methods and fields into someone's arsenal without >>> either >>> * pulling the veil and explaining the anonymous class. This requires >>> static imports. We've looped. >>> * leaving them with guidance like "you can't make classes, records work >>> though. I'll tell you later why. Just do math type stuff here." >>> >>> Then there is the confounding factor of *why* they would want a static >>> method. In "real code" we use static methods and fields for >>> >>> * globals >>> * "pure" logic >>> * factory methods >>> >>> Before they move to another file, they already have globals and they can >>> already put pure logic at the top level. Factory methods only make sense >>> once you've introduced large program topics like visibility. >>> >>> I.E. you only need factory methods if >>> >>> 1. You want to hide a constructor >>> >>> class Point { >>> private Point(int x, int y) {} >>> >>> static Point of(int x, int y) { >>> return new Point(x, y); >>> } >>> } >>> >>> But this is a nuanced api topic and isn't even relevant when everything >>> is a nestmate. Really a "large program" sort of choice. >>> >>> 2. You want to create through an interface >>> >>> interface LinkedList { >>> int size(); >>> >>> static LinkedList empty() { >>> return new EmptyList(); >>> } >>> >>> static LinkedList notEmpty(int head, LinkedList tail) { >>> >>> } >>> } >>> >>> class EmptyList implements LinkedList { >>> @Override >>> public int size() { >>> return 0; >>> } >>> } >>> >>> But as you see from the code you need to have public to teach >>> interfaces. You can gloss over it (my school did) but it's there. Even then >>> "why not just new EmptyList()" has an answer really rooted in (again) large >>> program concerns. You make code for potentially an external audience and >>> need to give a "good" API. >>> >>> 3. You want otherwise incompatible overloads >>> >>> class Point { >>> Point(int x, int y) {} >>> >>> static Point ofX(int x) { >>> return new Point(x, 0); >>> } >>> >>> static Point ofY(int y) { >>> return new Point(0, y); >>> } >>> } >>> >>> Which does work a little. But it doesn't really give a good opener for >>> static imports. The method names you'd give here are like ofX, fromX. You >>> would want to use those qualified every time. You also activate the >>> footguns I showed above. >>> >>> int pointsMade = 0; >>> >>> class Point { >>> Point(int x, int y) {} >>> >>> static Point ofX(int x) { >>> pointsMade++; >>> return new Point(x, 0); >>> } >>> >>> static Point ofY(int y) { >>> pointsMade++; >>> return new Point(0, y); >>> } >>> } >>> >>> -- >>> >>> > I would always start by taking one of their projects, ask them to >>> tweak in a subtle, but difficult way, then let them run headfirst into the >>> problem. Once they start to feel the strain, I would introduce them to the >>> concepts of classes. >>> >>> I think this is what I am missing. Without going into large or >>> multi-file program design there isn't much reason to use static anything. *In >>> addition* to that there are mechanical difficulties that can only be >>> explained properly once you reach a 2nd file. >>> >>> I.E. - "you were actually using a class until now. For true globals use >>> static fields", "For methods you can call anywhere use static" are the >>> explanations I want to give. Both of those explanations need multiple files >>> already in place to make sense. >>> >>> Does that track? >>> >>> >>> >>> On Sat, Jun 1, 2024 at 10:47?PM David Alayachew < >>> davidalayachew at gmail.com> wrote: >>> >>>> >>>> Heh, we're both unpaid :P I tutor folks from my university and church, >>>> as well as a couple of friends and family. Slowing down now though because >>>> of workload at work. >>>> >>>> But back to the point. >>>> >>>> > I think the problem is that without static "global" >>>> > fields are always accessible and all the nested classes >>>> > in their program are instansable from anywhere. >>>> > >>>> > Once you have a static method, a method on a record, a >>>> > method on an enum, or a static inner class this is no >>>> > longer the case. >>>> >>>> Apologies, maybe I missed the memo. I have been out of touch with this >>>> (and all other JEP's) because of my insane workload for the past couple of >>>> months. >>>> >>>> Why would adding a static method change any of the logic? >>>> >>>> I took your example in Java 22, added a static method, and everything >>>> worked dandy. >>>> >>>> Am I missing something? >>>> >>>> On Sat, Jun 1, 2024 at 9:28?PM Ethan McCue wrote: >>>> >>>>> I am also basically just a tutor, just unpaid. >>>>> >>>>> I think the problem is that without static "global" fields are always >>>>> accessible and all the nested classes in their program are instansable from >>>>> anywhere. >>>>> >>>>> Once you have a static method, a method on a record, a method on an >>>>> enum, or a static inner class this is no longer the case. >>>>> >>>>> Trite example of the mechanics I'm referencing: >>>>> >>>>> int count = 0; >>>>> >>>>> class A { >>>>> void b() { >>>>> count++; >>>>> c(); >>>>> new D(); >>>>> } >>>>> } >>>>> >>>>> void c() { >>>>> println("" + count); >>>>> } >>>>> >>>>> class D {} >>>>> >>>>> void main() { >>>>> new A().b(); >>>>> } >>>>> >>>>> So when you show static methods, I think you need to understand why >>>>> count++; and c(); and new D(); would not function. >>>>> >>>>> That requires knowing that you are actually in an anonymous class. >>>>> That requires we get past import static java.io.IO. That's the loop. >>>>> >>>>> >>>>> >>>>> On Sat, Jun 1, 2024, 8:59 PM David Alayachew >>>>> wrote: >>>>> >>>>>> Maybe I am missing something, but a static import seems like the >>>>>> natural next step to teaching a student who made it that far. >>>>>> >>>>>> If a student feels the need to break out of the bounds of an >>>>>> implicitly declared class, they must first understand what a class is going >>>>>> to save them from. In this case, it is allowing code they right to be >>>>>> reused else where. >>>>>> >>>>>> I am only a tutor, not a full-blown teacher, but when tutoring folks >>>>>> in this exact subject, I would always start by taking one of their >>>>>> projects, ask them to tweak in a subtle, but difficult way, then let them >>>>>> run headfirst into the problem. Once they start to feel the strain, I would >>>>>> introduce them to the concepts of classes. >>>>>> >>>>>> I would start by showing them how to create a simple pure function, >>>>>> similar to java.lang.Math. From there, we would make a few pure functions >>>>>> that are unrelated to their current task, and then have them get >>>>>> comfortable with that concept. >>>>>> >>>>>> Then, we would make another pure function that is being done >>>>>> repeatedly in their "JumboClass1" and "JumboClass2", and then have them get >>>>>> comfortable using this helper method in both versions. >>>>>> >>>>>> In my mind, this is naturally where the student would discover that >>>>>> they need to do a static import. In which case, they are in the middle of >>>>>> understanding a class, they understand imports, and they understand static >>>>>> methods fairly well. So, a static import has been well prepared for them. >>>>>> >>>>>> For me, this is a neat and clean introduction. Maybe I am missing >>>>>> something? >>>>>> >>>>>> On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler >>>>>> wrote: >>>>>> >>>>>>> Hi. >>>>>>> >>>>>>> Without getting into the merits of not implicitly importing >>>>>>> anything, let me just point out that the following is a valid program under >>>>>>> the current JEP: >>>>>>> >>>>>>> void main() { >>>>>>> IO.println(?Hello, world!?); >>>>>>> } >>>>>>> >>>>>>> As is this one: >>>>>>> >>>>>>> import module java.base; >>>>>>> >>>>>>> void main() { >>>>>>> IO.println(?Hello, world!?); >>>>>>> } >>>>>>> >>>>>>> In other words, the fact that there are implicit imports doesn?t >>>>>>> mean that you can?t ignore them or add them explicitly. if you find >>>>>>> teaching this to be easier, you can. So even if you don?t find implicit >>>>>>> imports helpful, they may be helpful to other teachers, who may not want to >>>>>>> start with some import incantation that necessarily implies some >>>>>>> programming-in-the-large concept. >>>>>>> >>>>>>> But I think that your unease mostly stems from the extra magic that >>>>>>> implicit classes enjoy, and which isn?t strictly necessitated by their >>>>>>> primary quality of being implicitly declared classes, and that extra magic >>>>>>> differentiates them from regular compilation units along some other axis; >>>>>>> is that right? >>>>>>> >>>>>>> ? Ron >>>>>>> >>>>>>> >>>>>>> >>>>>>> > On 1 Jun 2024, at 23:14, Ethan McCue >>>>>>> wrote: >>>>>>> > >>>>>>> > Hi all, >>>>>>> > >>>>>>> > I'm following the development of JEP 477[1] and I feel the need to >>>>>>> question the impetus for the implicit static imports. >>>>>>> > >>>>>>> > As of now[2] any program like this >>>>>>> > >>>>>>> > void main() { >>>>>>> > println("Hello, world"); >>>>>>> > } >>>>>>> > >>>>>>> > Is equivalent to >>>>>>> > >>>>>>> > import static java.io.IO.print; >>>>>>> > import static java.io.IO.println; >>>>>>> > import static java.io.IO.writeln; >>>>>>> > >>>>>>> > import module java.base; >>>>>>> > >>>>>>> > final class Main { >>>>>>> > void main() { >>>>>>> > println("Hello, world"); >>>>>>> > } >>>>>>> > } >>>>>>> > >>>>>>> > Where all the methods in java.io.IO delegate to newly added >>>>>>> equivalent methods in java.io.Console.[3] >>>>>>> > >>>>>>> > Aside from muddying that API up (now there is readln and readLine >>>>>>> + println and printLine which...what) I'm still concerned on how those >>>>>>> implicit imports will affect the transition to named classes. >>>>>>> > >>>>>>> > Assume we start a student out here >>>>>>> > >>>>>>> > void main() { >>>>>>> > println("Hello, world"); >>>>>>> > } >>>>>>> > >>>>>>> > You can get through conditionals, loops, variables, methods, and >>>>>>> return types before touching classes or access specifiers. >>>>>>> > >>>>>>> > int compute() { >>>>>>> > int total = 0; >>>>>>> > for (int i = 0; i < 10; i++) { >>>>>>> > total += i; >>>>>>> > } >>>>>>> > return total; >>>>>>> > } >>>>>>> > >>>>>>> > void main() { >>>>>>> > println("Hello: " + compute()); >>>>>>> > } >>>>>>> > >>>>>>> > You can even talk about records and enums in a hand wavey way. >>>>>>> Enums are "one of these options", Records are "if you want to return two >>>>>>> things." >>>>>>> > >>>>>>> > enum Pirate { >>>>>>> > BLACKBEARD, >>>>>>> > OTHER >>>>>>> > } >>>>>>> > >>>>>>> > record Pos(int x, int y) {} >>>>>>> > >>>>>>> > Pos treasure(Pirate pirate) { >>>>>>> > switch (pirate) { >>>>>>> > case BLACKBEARD -> >>>>>>> > return new Pos(5, 5); >>>>>>> > case OTHER -> >>>>>>> > return new Pos(0, 0); >>>>>>> > } >>>>>>> > } >>>>>>> > >>>>>>> > void main() { >>>>>>> > println(treasure(Pirate.OTHER)); >>>>>>> > } >>>>>>> > >>>>>>> > So it is reasonable for a student to have made a relatively >>>>>>> complex program before having to get to that point, but I think you do need >>>>>>> to explain what exactly is going on with the anonymous main class when you >>>>>>> introduce multi file programs. >>>>>>> > >>>>>>> > As originally pitched, this transition would have just meant >>>>>>> wrapping the whole program in class Main {}, but now to make the transition >>>>>>> from >>>>>>> > >>>>>>> > void main() { >>>>>>> > // Arbitrary code >>>>>>> > } >>>>>>> > >>>>>>> > to >>>>>>> > >>>>>>> > class Main { >>>>>>> > void main() { >>>>>>> > // Arbitrary code >>>>>>> > } >>>>>>> > } >>>>>>> > >>>>>>> > In a robust way, you need to add those imports to the top. You can >>>>>>> skip the final since the semantics of extension haven't been relevant yet. >>>>>>> > >>>>>>> > import static java.io.IO.*; >>>>>>> > >>>>>>> > import module java.base; >>>>>>> > >>>>>>> > class Main { >>>>>>> > void main() { >>>>>>> > // Arbitrary code >>>>>>> > } >>>>>>> > } >>>>>>> > >>>>>>> > My gripe is that the concepts introduced here - static, module, >>>>>>> and * imports - would have had no place to be introduced earlier. >>>>>>> > >>>>>>> > If you have folks write static methods on inner classes, the >>>>>>> metaphor of a "global field" that otherwise exists in the simple-main world >>>>>>> goes away. >>>>>>> > >>>>>>> > // For every program up until this, they could freely access this >>>>>>> from anywhere >>>>>>> > int count = 0; >>>>>>> > >>>>>>> > // And they could freely make an instance of any inner class >>>>>>> > class Other {} >>>>>>> > >>>>>>> > class Pos { >>>>>>> > static Pos of(int x, int y) { >>>>>>> > // So the rules they are used to don't apply anymore >>>>>>> > // and the explanation as to why really lies *after* they >>>>>>> understand >>>>>>> > // what an anonymous main class is and does >>>>>>> > // ... >>>>>>> > } >>>>>>> > } >>>>>>> > >>>>>>> > void main() { >>>>>>> > // ... >>>>>>> > } >>>>>>> > >>>>>>> > If you have folks *use* static methods that is fine - the >>>>>>> hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure >>>>>>> out how to topologically sort topics such that import static makes any >>>>>>> sense before going through multi-file programs. >>>>>>> > >>>>>>> > I have a similar concern with import module, but that can be hand >>>>>>> waved as "gets you everything from this library" so I am less concerned. >>>>>>> Still don't fully understand the desire to have it be implicit, but less >>>>>>> concerned. >>>>>>> > >>>>>>> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new >>>>>>> import rules needed and now IO is available to all programs. >>>>>>> > >>>>>>> > void main() { >>>>>>> > IO.printLine("Hello, world"); >>>>>>> > } >>>>>>> > >>>>>>> > While this does introduce something not explained - static method >>>>>>> access - it feels easier to hand-wave away than import static java.io.IO.*; >>>>>>> would be. It calls the printLine method, it comes from IO. IO means >>>>>>> "Input/Output". That's a workable metaphor and can be glanced over in the >>>>>>> same way as Math.max >>>>>>> > >>>>>>> > The transition from here to classes can again be "it's the same as >>>>>>> if you had class Main {} around it" instead of "it's the same as if you had >>>>>>> class Main {} around it and these imports. A static import imports a static >>>>>>> method. A static method is a method attached to the class itself instead of >>>>>>> the instance.... etc." >>>>>>> > >>>>>>> > Also, IO. feels like a different kind of unexplained boilerplate >>>>>>> than public static void main(String[] args) {}. There is a lot of ground >>>>>>> you need to cover before access modifiers, static methods, or command line >>>>>>> arguments make any sort of sense. When you have someone write IO they are >>>>>>> in that moment producing output and will soon take input. >>>>>>> > >>>>>>> > What am I overlooking? >>>>>>> > >>>>>>> > >>>>>>> > [1]: https://openjdk.org/jeps/477 >>>>>>> > [2]: >>>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >>>>>>> > [3] >>>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >>>>>>> >>>>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Sun Jun 2 22:17:45 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Mon, 3 Jun 2024 01:17:45 +0300 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: Not sure if this conversation haven't shifted from its topic yet, but I would argue with the point that state is bad and you should prefer static methods wherever it is possible. There is a few layers to it. First and foremost, keeping some information in object state instead of passing it ap and down through long invocation chains is just simply better for readability. Extracting object is a most common refactoring to eliminate triads and higher order methods. Secondly, mutability isn't something inheritendly bad. What is bad is shared mutability. When mutations of state are encapsulated properly inside of the owning object, they aren't harmful at all. Moreover, properly dealing with state and mutability is one of the most important skills for developer. There is more to purity then just immutability, and furthermore, purity does not forbid mutability. Lastly, if students you are teaching would ever decide to become commercial developers, they would be inevitably struck with a fact that static methods here are almost considered antipattern and discarded in favor of singletone. So, to conclude, it is not about how far you can go without state, it is about how far you should go. Not even mentioning the fact that static methods provide no guarantee of purity, and instance methods do not imply impurity. Functional style now is so exaggerated people often forget why oop took over the world in the first place On Mon, Jun 3, 2024, 00:06 David Alayachew wrote: > Sometimes, I don't get the choice to avoid state. But when the opportunity > arises/the assignment doesn't require it, I will even encourage the student > to undo some work TO AVOID using state. > > So no, if I get the chance, I avoid it every chance I get. > > As for static methods, I don't usually get to choose for them. The choice > has usually been made, and there's a cost to undoing too much of their work. > > That said, most of what we work with is instance methods with as little > state as possible. > > But to be clear, the most interaction they have with INSTANCES is calling > it in their main method. Again, the goal is to get them using functional > for as long as possible. > > Sad to see dev.java on the list, but I have to agree with you. Never > considered it to be a beginner resource though. > > And as for being forced to eventually introduce state, you'd be shocked > just how far we can go with it lol. I can usually get them to GUI's before > I get forced into it. And even then, I don't let them introduce any state > of their own, just modifying a JFrame and stuff along that order, which is > incredibly easy to maintain for the kids. > > On Sun, Jun 2, 2024 at 3:36?PM Ethan McCue wrote: > >> I'm pondering a bit so this isn't too deep of a response >> >> > Their JumboClassA is very big and unwieldy, and they want to use parts >> of it for JumboClassB, and maybe other things I've introduced them to at >> that point. If I haven't already, I introduce them to the Math class, show >> them how it works, let them make a HelloWorld that does something >> meaningless, let them call it in their existing class, let them call it in >> another project, let them make a Math2, and then from there, they usually >> feel comfortable enough to try and extract a method from what they were >> actually doing into a pure, static function. >> >> Are you saying that you only have them write static methods to start >> with? Like >> >> class JumboClassA { >> static int code() { >> return JumboClassB.compute() + 1; >> } >> } >> >> class JumboClassB { >> static int compute() { >> return 0; >> } >> } >> >> If so, do you oft get around to instances and instance state? >> >> > Long story short, you and I approach tutoring in WILDLY different ways. >> >> Also in different contexts. I spend most of my time in one of a few >> discord channels answering questions and that sometimes leads to people who >> I tutor over a longer period of time. We mostly get people who are either >> struggling with an existing curriculum and try to do course correction (I >> started making a shortlist of the bad college curriculums we are exposed >> to). >> >> So the things we notice are that current curriculums either >> * Have ordering issues within them. Explaining JPA before SQL, Abstract >> classes before interfaces, Large program design before small, etc. >> * Have no clear next steps. Most intro courses, after taking a deep sigh >> of relief for having gotten a successful for loop out of a teenager, just >> sorta end. >> >> So we have very few Java courses (that we know of) to point people to >> that actually explain the topics they introduce in the order they will be >> encountered. (See https://dev.java/learn/ , >> https://www.baeldung.com/java-tutorial, >> https://www.geeksforgeeks.org/java/ for examples of terrible tutorials) >> >> And after we give them something like https://java-programming.mooc.fi/ >> and they finish we have nowhere to go from there. That's part of why I >> think topic order matters so much. >> >> > I have only tutored one student using this implicit class feature so >> far, and we are nowhere near this point yet. >> >> Take notes on how that progresses + any differences from your previous >> approach. >> >> > I guess what I am saying is that, since I don't really depend on static >> or instance level state, I just don't have this problem. And by the time I >> do, they have gotten so used to doing functional, that any interaction with >> global state stands out as something to watch out for. >> >> I went to Northeastern and their intro curriculum starts you with Racket >> doing really rigid comments that drills in sum types + recursion. Then they >> introduce Java without mutation at all for 80% of the 2nd semester. You >> only get mutation at the end and at that point your brain was broken from >> all the purity you forget how. >> >> So I get it. But eventually those concepts need to be introduced. >> >> >> On Sun, Jun 2, 2024 at 12:06?AM David Alayachew >> wrote: >> >>> Well, I definitely see the discrepancy now. Yes, this makes a lot of >>> sense. >>> >>> Apologies for not recognizing your point earlier, I had assumed that you >>> were talking about some fundamental execution change that the kid needs to >>> be aware of. I didn't realize that you were talking about potholes that the >>> kid might fall into. >>> >>> Long story short, you and I approach tutoring in WILDLY different ways. >>> >>> First and foremost, the concept of introducing instance state this early >>> on is dangerous in my eyes. If you have a kid that can dance at that tempo, >>> then you truly have a gifted kid. But from my 10+ years of tutoring, EVERY >>> SINGLE TIME I try to introduce them to the idea of having state ANYWHERE >>> except for inside of a method, things fall apart. It will be a long time >>> before I ever try and get this kid to try and manage state at the instance >>> level. >>> >>> I approach it very differently. >>> >>> I start off teaching these kids to approach problems with a functional >>> mindset. Pure functions everywhere. This usually ends up creating a big, >>> singular essay of a function, and once they start to feel the strain from >>> that, I show them how to thread their state in through methods and return >>> values. >>> >>> Anyways, back to my example from 2 emails ago. >>> >>> Their JumboClassA is very big and unwieldy, and they want to use parts >>> of it for JumboClassB, and maybe other things I've introduced them to at >>> that point. If I haven't already, I introduce them to the Math class, show >>> them how it works, let them make a HelloWorld that does something >>> meaningless, let them call it in their existing class, let them call it in >>> another project, let them make a Math2, and then from there, they usually >>> feel comfortable enough to try and extract a method from what they were >>> actually doing into a pure, static function. >>> >>> I have only tutored one student using this implicit class feature so >>> far, and we are nowhere near this point yet. But I expect that the only >>> thing that will change before I get there is explaining to them how, in >>> order to use their methods outside of the class, they need to give the >>> class an explicit name. I foresee no pain points with that. After all, how >>> can you reference a class that does not have an explicit name? And I expect >>> them to understand the unidirectionality of that. A class without a name >>> can use methods from a class WITH a name. But the reverse is false. I'm >>> actually sorted of excited to spring that onto the kids and see how they >>> react lol. >>> >>> Really, the only issue I ever run into is when kids ask me, what is >>> static useful for anyways? They usually feel like they only need static or >>> instance methods. >>> >>> I guess what I am saying is that, since I don't really depend on static >>> or instance level state, I just don't have this problem. And by the time I >>> do, they have gotten so used to doing functional, that any interaction with >>> global state stands out as something to watch out for. From there, they >>> understand how much of an ordeal it is to work with state, and exactly how >>> dangerous and error-prone that it is that I can usually get them to turn >>> their alertness up to 11 whenever the need arises. >>> >>> Let me first get your perspective though before I continue my point. >>> What are your thoughts on this? >>> >>> And apologies for not responding to your points individually. Our >>> assumptions are on opposite ends, so there is very little that I can >>> directly respond to. >>> >>> On Sat, Jun 1, 2024 at 11:22?PM Ethan McCue wrote: >>> >>>> I really should find a better example / adapt something in an existing >>>> curriculum - but: >>>> >>>> Mutation 1: Make b static >>>> >>>> int count = 0; >>>> >>>> class A { >>>> static void b() { >>>> count++; >>>> c(); >>>> new D(); >>>> } >>>> } >>>> >>>> void c() { >>>> println("" + count); >>>> } >>>> >>>> class D {} >>>> >>>> void main() { >>>> new A().b(); >>>> } >>>> >>>> >>>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=be416fe8e6df84c81c0f5526f0e28aef >>>> >>>> Mutation 2: Make c static >>>> >>>> int count = 0; >>>> >>>> class A { >>>> void b() { >>>> count++; >>>> c(); >>>> new D(); >>>> } >>>> } >>>> >>>> static void c() { >>>> println("" + count); >>>> } >>>> >>>> class D {} >>>> >>>> void main() { >>>> new A().b(); >>>> } >>>> >>>> >>>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=b71e9b94cff6099afb5153abffdbc3e9 >>>> >>>> Mutation 3: (for fun) Make A a record >>>> >>>> int count = 0; >>>> >>>> record A() { >>>> void b() { >>>> count++; >>>> c(); >>>> new D(); >>>> } >>>> } >>>> >>>> void c() { >>>> println("" + count); >>>> } >>>> >>>> class D {} >>>> >>>> void main() { >>>> new A().b(); >>>> } >>>> >>>> >>>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=cb5bf4664bf83ffc8ee4e5fe85abadb6 >>>> >>>> Maybe it's just a failure of imagination, but I don't know how to >>>> introduce static methods and fields into someone's arsenal without >>>> either >>>> * pulling the veil and explaining the anonymous class. This requires >>>> static imports. We've looped. >>>> * leaving them with guidance like "you can't make classes, records work >>>> though. I'll tell you later why. Just do math type stuff here." >>>> >>>> Then there is the confounding factor of *why* they would want a static >>>> method. In "real code" we use static methods and fields for >>>> >>>> * globals >>>> * "pure" logic >>>> * factory methods >>>> >>>> Before they move to another file, they already have globals and they >>>> can already put pure logic at the top level. Factory methods only make >>>> sense once you've introduced large program topics like visibility. >>>> >>>> I.E. you only need factory methods if >>>> >>>> 1. You want to hide a constructor >>>> >>>> class Point { >>>> private Point(int x, int y) {} >>>> >>>> static Point of(int x, int y) { >>>> return new Point(x, y); >>>> } >>>> } >>>> >>>> But this is a nuanced api topic and isn't even relevant when everything >>>> is a nestmate. Really a "large program" sort of choice. >>>> >>>> 2. You want to create through an interface >>>> >>>> interface LinkedList { >>>> int size(); >>>> >>>> static LinkedList empty() { >>>> return new EmptyList(); >>>> } >>>> >>>> static LinkedList notEmpty(int head, LinkedList tail) { >>>> >>>> } >>>> } >>>> >>>> class EmptyList implements LinkedList { >>>> @Override >>>> public int size() { >>>> return 0; >>>> } >>>> } >>>> >>>> But as you see from the code you need to have public to teach >>>> interfaces. You can gloss over it (my school did) but it's there. Even then >>>> "why not just new EmptyList()" has an answer really rooted in (again) large >>>> program concerns. You make code for potentially an external audience and >>>> need to give a "good" API. >>>> >>>> 3. You want otherwise incompatible overloads >>>> >>>> class Point { >>>> Point(int x, int y) {} >>>> >>>> static Point ofX(int x) { >>>> return new Point(x, 0); >>>> } >>>> >>>> static Point ofY(int y) { >>>> return new Point(0, y); >>>> } >>>> } >>>> >>>> Which does work a little. But it doesn't really give a good opener for >>>> static imports. The method names you'd give here are like ofX, fromX. You >>>> would want to use those qualified every time. You also activate the >>>> footguns I showed above. >>>> >>>> int pointsMade = 0; >>>> >>>> class Point { >>>> Point(int x, int y) {} >>>> >>>> static Point ofX(int x) { >>>> pointsMade++; >>>> return new Point(x, 0); >>>> } >>>> >>>> static Point ofY(int y) { >>>> pointsMade++; >>>> return new Point(0, y); >>>> } >>>> } >>>> >>>> -- >>>> >>>> > I would always start by taking one of their projects, ask them to >>>> tweak in a subtle, but difficult way, then let them run headfirst into the >>>> problem. Once they start to feel the strain, I would introduce them to the >>>> concepts of classes. >>>> >>>> I think this is what I am missing. Without going into large or >>>> multi-file program design there isn't much reason to use static anything. *In >>>> addition* to that there are mechanical difficulties that can only be >>>> explained properly once you reach a 2nd file. >>>> >>>> I.E. - "you were actually using a class until now. For true globals use >>>> static fields", "For methods you can call anywhere use static" are the >>>> explanations I want to give. Both of those explanations need multiple files >>>> already in place to make sense. >>>> >>>> Does that track? >>>> >>>> >>>> >>>> On Sat, Jun 1, 2024 at 10:47?PM David Alayachew < >>>> davidalayachew at gmail.com> wrote: >>>> >>>>> >>>>> Heh, we're both unpaid :P I tutor folks from my university and church, >>>>> as well as a couple of friends and family. Slowing down now though because >>>>> of workload at work. >>>>> >>>>> But back to the point. >>>>> >>>>> > I think the problem is that without static "global" >>>>> > fields are always accessible and all the nested classes >>>>> > in their program are instansable from anywhere. >>>>> > >>>>> > Once you have a static method, a method on a record, a >>>>> > method on an enum, or a static inner class this is no >>>>> > longer the case. >>>>> >>>>> Apologies, maybe I missed the memo. I have been out of touch with this >>>>> (and all other JEP's) because of my insane workload for the past couple of >>>>> months. >>>>> >>>>> Why would adding a static method change any of the logic? >>>>> >>>>> I took your example in Java 22, added a static method, and everything >>>>> worked dandy. >>>>> >>>>> Am I missing something? >>>>> >>>>> On Sat, Jun 1, 2024 at 9:28?PM Ethan McCue wrote: >>>>> >>>>>> I am also basically just a tutor, just unpaid. >>>>>> >>>>>> I think the problem is that without static "global" fields are always >>>>>> accessible and all the nested classes in their program are instansable from >>>>>> anywhere. >>>>>> >>>>>> Once you have a static method, a method on a record, a method on an >>>>>> enum, or a static inner class this is no longer the case. >>>>>> >>>>>> Trite example of the mechanics I'm referencing: >>>>>> >>>>>> int count = 0; >>>>>> >>>>>> class A { >>>>>> void b() { >>>>>> count++; >>>>>> c(); >>>>>> new D(); >>>>>> } >>>>>> } >>>>>> >>>>>> void c() { >>>>>> println("" + count); >>>>>> } >>>>>> >>>>>> class D {} >>>>>> >>>>>> void main() { >>>>>> new A().b(); >>>>>> } >>>>>> >>>>>> So when you show static methods, I think you need to understand why >>>>>> count++; and c(); and new D(); would not function. >>>>>> >>>>>> That requires knowing that you are actually in an anonymous class. >>>>>> That requires we get past import static java.io.IO. That's the loop. >>>>>> >>>>>> >>>>>> >>>>>> On Sat, Jun 1, 2024, 8:59 PM David Alayachew < >>>>>> davidalayachew at gmail.com> wrote: >>>>>> >>>>>>> Maybe I am missing something, but a static import seems like the >>>>>>> natural next step to teaching a student who made it that far. >>>>>>> >>>>>>> If a student feels the need to break out of the bounds of an >>>>>>> implicitly declared class, they must first understand what a class is going >>>>>>> to save them from. In this case, it is allowing code they right to be >>>>>>> reused else where. >>>>>>> >>>>>>> I am only a tutor, not a full-blown teacher, but when tutoring folks >>>>>>> in this exact subject, I would always start by taking one of their >>>>>>> projects, ask them to tweak in a subtle, but difficult way, then let them >>>>>>> run headfirst into the problem. Once they start to feel the strain, I would >>>>>>> introduce them to the concepts of classes. >>>>>>> >>>>>>> I would start by showing them how to create a simple pure function, >>>>>>> similar to java.lang.Math. From there, we would make a few pure functions >>>>>>> that are unrelated to their current task, and then have them get >>>>>>> comfortable with that concept. >>>>>>> >>>>>>> Then, we would make another pure function that is being done >>>>>>> repeatedly in their "JumboClass1" and "JumboClass2", and then have them get >>>>>>> comfortable using this helper method in both versions. >>>>>>> >>>>>>> In my mind, this is naturally where the student would discover that >>>>>>> they need to do a static import. In which case, they are in the middle of >>>>>>> understanding a class, they understand imports, and they understand static >>>>>>> methods fairly well. So, a static import has been well prepared for them. >>>>>>> >>>>>>> For me, this is a neat and clean introduction. Maybe I am missing >>>>>>> something? >>>>>>> >>>>>>> On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler >>>>>>> wrote: >>>>>>> >>>>>>>> Hi. >>>>>>>> >>>>>>>> Without getting into the merits of not implicitly importing >>>>>>>> anything, let me just point out that the following is a valid program under >>>>>>>> the current JEP: >>>>>>>> >>>>>>>> void main() { >>>>>>>> IO.println(?Hello, world!?); >>>>>>>> } >>>>>>>> >>>>>>>> As is this one: >>>>>>>> >>>>>>>> import module java.base; >>>>>>>> >>>>>>>> void main() { >>>>>>>> IO.println(?Hello, world!?); >>>>>>>> } >>>>>>>> >>>>>>>> In other words, the fact that there are implicit imports doesn?t >>>>>>>> mean that you can?t ignore them or add them explicitly. if you find >>>>>>>> teaching this to be easier, you can. So even if you don?t find implicit >>>>>>>> imports helpful, they may be helpful to other teachers, who may not want to >>>>>>>> start with some import incantation that necessarily implies some >>>>>>>> programming-in-the-large concept. >>>>>>>> >>>>>>>> But I think that your unease mostly stems from the extra magic that >>>>>>>> implicit classes enjoy, and which isn?t strictly necessitated by their >>>>>>>> primary quality of being implicitly declared classes, and that extra magic >>>>>>>> differentiates them from regular compilation units along some other axis; >>>>>>>> is that right? >>>>>>>> >>>>>>>> ? Ron >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> > On 1 Jun 2024, at 23:14, Ethan McCue >>>>>>>> wrote: >>>>>>>> > >>>>>>>> > Hi all, >>>>>>>> > >>>>>>>> > I'm following the development of JEP 477[1] and I feel the need >>>>>>>> to question the impetus for the implicit static imports. >>>>>>>> > >>>>>>>> > As of now[2] any program like this >>>>>>>> > >>>>>>>> > void main() { >>>>>>>> > println("Hello, world"); >>>>>>>> > } >>>>>>>> > >>>>>>>> > Is equivalent to >>>>>>>> > >>>>>>>> > import static java.io.IO.print; >>>>>>>> > import static java.io.IO.println; >>>>>>>> > import static java.io.IO.writeln; >>>>>>>> > >>>>>>>> > import module java.base; >>>>>>>> > >>>>>>>> > final class Main { >>>>>>>> > void main() { >>>>>>>> > println("Hello, world"); >>>>>>>> > } >>>>>>>> > } >>>>>>>> > >>>>>>>> > Where all the methods in java.io.IO delegate to newly added >>>>>>>> equivalent methods in java.io.Console.[3] >>>>>>>> > >>>>>>>> > Aside from muddying that API up (now there is readln and readLine >>>>>>>> + println and printLine which...what) I'm still concerned on how those >>>>>>>> implicit imports will affect the transition to named classes. >>>>>>>> > >>>>>>>> > Assume we start a student out here >>>>>>>> > >>>>>>>> > void main() { >>>>>>>> > println("Hello, world"); >>>>>>>> > } >>>>>>>> > >>>>>>>> > You can get through conditionals, loops, variables, methods, and >>>>>>>> return types before touching classes or access specifiers. >>>>>>>> > >>>>>>>> > int compute() { >>>>>>>> > int total = 0; >>>>>>>> > for (int i = 0; i < 10; i++) { >>>>>>>> > total += i; >>>>>>>> > } >>>>>>>> > return total; >>>>>>>> > } >>>>>>>> > >>>>>>>> > void main() { >>>>>>>> > println("Hello: " + compute()); >>>>>>>> > } >>>>>>>> > >>>>>>>> > You can even talk about records and enums in a hand wavey way. >>>>>>>> Enums are "one of these options", Records are "if you want to return two >>>>>>>> things." >>>>>>>> > >>>>>>>> > enum Pirate { >>>>>>>> > BLACKBEARD, >>>>>>>> > OTHER >>>>>>>> > } >>>>>>>> > >>>>>>>> > record Pos(int x, int y) {} >>>>>>>> > >>>>>>>> > Pos treasure(Pirate pirate) { >>>>>>>> > switch (pirate) { >>>>>>>> > case BLACKBEARD -> >>>>>>>> > return new Pos(5, 5); >>>>>>>> > case OTHER -> >>>>>>>> > return new Pos(0, 0); >>>>>>>> > } >>>>>>>> > } >>>>>>>> > >>>>>>>> > void main() { >>>>>>>> > println(treasure(Pirate.OTHER)); >>>>>>>> > } >>>>>>>> > >>>>>>>> > So it is reasonable for a student to have made a relatively >>>>>>>> complex program before having to get to that point, but I think you do need >>>>>>>> to explain what exactly is going on with the anonymous main class when you >>>>>>>> introduce multi file programs. >>>>>>>> > >>>>>>>> > As originally pitched, this transition would have just meant >>>>>>>> wrapping the whole program in class Main {}, but now to make the transition >>>>>>>> from >>>>>>>> > >>>>>>>> > void main() { >>>>>>>> > // Arbitrary code >>>>>>>> > } >>>>>>>> > >>>>>>>> > to >>>>>>>> > >>>>>>>> > class Main { >>>>>>>> > void main() { >>>>>>>> > // Arbitrary code >>>>>>>> > } >>>>>>>> > } >>>>>>>> > >>>>>>>> > In a robust way, you need to add those imports to the top. You >>>>>>>> can skip the final since the semantics of extension haven't been relevant >>>>>>>> yet. >>>>>>>> > >>>>>>>> > import static java.io.IO.*; >>>>>>>> > >>>>>>>> > import module java.base; >>>>>>>> > >>>>>>>> > class Main { >>>>>>>> > void main() { >>>>>>>> > // Arbitrary code >>>>>>>> > } >>>>>>>> > } >>>>>>>> > >>>>>>>> > My gripe is that the concepts introduced here - static, module, >>>>>>>> and * imports - would have had no place to be introduced earlier. >>>>>>>> > >>>>>>>> > If you have folks write static methods on inner classes, the >>>>>>>> metaphor of a "global field" that otherwise exists in the simple-main world >>>>>>>> goes away. >>>>>>>> > >>>>>>>> > // For every program up until this, they could freely access this >>>>>>>> from anywhere >>>>>>>> > int count = 0; >>>>>>>> > >>>>>>>> > // And they could freely make an instance of any inner class >>>>>>>> > class Other {} >>>>>>>> > >>>>>>>> > class Pos { >>>>>>>> > static Pos of(int x, int y) { >>>>>>>> > // So the rules they are used to don't apply anymore >>>>>>>> > // and the explanation as to why really lies *after* they >>>>>>>> understand >>>>>>>> > // what an anonymous main class is and does >>>>>>>> > // ... >>>>>>>> > } >>>>>>>> > } >>>>>>>> > >>>>>>>> > void main() { >>>>>>>> > // ... >>>>>>>> > } >>>>>>>> > >>>>>>>> > If you have folks *use* static methods that is fine - the >>>>>>>> hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure >>>>>>>> out how to topologically sort topics such that import static makes any >>>>>>>> sense before going through multi-file programs. >>>>>>>> > >>>>>>>> > I have a similar concern with import module, but that can be hand >>>>>>>> waved as "gets you everything from this library" so I am less concerned. >>>>>>>> Still don't fully understand the desire to have it be implicit, but less >>>>>>>> concerned. >>>>>>>> > >>>>>>>> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new >>>>>>>> import rules needed and now IO is available to all programs. >>>>>>>> > >>>>>>>> > void main() { >>>>>>>> > IO.printLine("Hello, world"); >>>>>>>> > } >>>>>>>> > >>>>>>>> > While this does introduce something not explained - static method >>>>>>>> access - it feels easier to hand-wave away than import static java.io.IO.*; >>>>>>>> would be. It calls the printLine method, it comes from IO. IO means >>>>>>>> "Input/Output". That's a workable metaphor and can be glanced over in the >>>>>>>> same way as Math.max >>>>>>>> > >>>>>>>> > The transition from here to classes can again be "it's the same >>>>>>>> as if you had class Main {} around it" instead of "it's the same as if you >>>>>>>> had class Main {} around it and these imports. A static import imports a >>>>>>>> static method. A static method is a method attached to the class itself >>>>>>>> instead of the instance.... etc." >>>>>>>> > >>>>>>>> > Also, IO. feels like a different kind of unexplained boilerplate >>>>>>>> than public static void main(String[] args) {}. There is a lot of ground >>>>>>>> you need to cover before access modifiers, static methods, or command line >>>>>>>> arguments make any sort of sense. When you have someone write IO they are >>>>>>>> in that moment producing output and will soon take input. >>>>>>>> > >>>>>>>> > What am I overlooking? >>>>>>>> > >>>>>>>> > >>>>>>>> > [1]: https://openjdk.org/jeps/477 >>>>>>>> > [2]: >>>>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >>>>>>>> > [3] >>>>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >>>>>>>> >>>>>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Jun 2 22:29:34 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 2 Jun 2024 18:29:34 -0400 Subject: Do we really need an implicit "import static java.io.IO.*"? In-Reply-To: References: Message-ID: You may be right that we are delving into tangential waters. I'll leave it at this -- I think this feature, as implemented, is perfectly approachable for new students doing it my way. I see a smooth ramp all the way up doing it the functional way. However, I do acknowledge that, if you take a state based approach, then there is a non-trivial speed bump near the halfway point. As for whether or not to remove it and how, I leave to you all. But if we were to remove it the way Ethan suggested, my strategy loses nothing. If the change doesn't hurt me and helps him, then I have no problem with it. On Sun, Jun 2, 2024 at 6:17?PM Olexandr Rotan wrote: > Not sure if this conversation haven't shifted from its topic yet, but I > would argue with the point that state is bad and you should prefer static > methods wherever it is possible. > > There is a few layers to it. First and foremost, keeping some information > in object state instead of passing it ap and down through long invocation > chains is just simply better for readability. Extracting object is a most > common refactoring to eliminate triads and higher order methods. > > Secondly, mutability isn't something inheritendly bad. What is bad is > shared mutability. When mutations of state are encapsulated properly inside > of the owning object, they aren't harmful at all. Moreover, properly > dealing with state and mutability is one of the most important skills for > developer. There is more to purity then just immutability, and furthermore, > purity does not forbid mutability. > > Lastly, if students you are teaching would ever decide to become > commercial developers, they would be inevitably struck with a fact that > static methods here are almost considered antipattern and discarded in > favor of singletone. > > So, to conclude, it is not about how far you can go without state, it is > about how far you should go. Not even mentioning the fact that static > methods provide no guarantee of purity, and instance methods do not imply > impurity. Functional style now is so exaggerated people often forget why > oop took over the world in the first place > > On Mon, Jun 3, 2024, 00:06 David Alayachew > wrote: > >> Sometimes, I don't get the choice to avoid state. But when the >> opportunity arises/the assignment doesn't require it, I will even encourage >> the student to undo some work TO AVOID using state. >> >> So no, if I get the chance, I avoid it every chance I get. >> >> As for static methods, I don't usually get to choose for them. The choice >> has usually been made, and there's a cost to undoing too much of their work. >> >> That said, most of what we work with is instance methods with as little >> state as possible. >> >> But to be clear, the most interaction they have with INSTANCES is calling >> it in their main method. Again, the goal is to get them using functional >> for as long as possible. >> >> Sad to see dev.java on the list, but I have to agree with you. Never >> considered it to be a beginner resource though. >> >> And as for being forced to eventually introduce state, you'd be shocked >> just how far we can go with it lol. I can usually get them to GUI's before >> I get forced into it. And even then, I don't let them introduce any state >> of their own, just modifying a JFrame and stuff along that order, which is >> incredibly easy to maintain for the kids. >> >> On Sun, Jun 2, 2024 at 3:36?PM Ethan McCue wrote: >> >>> I'm pondering a bit so this isn't too deep of a response >>> >>> > Their JumboClassA is very big and unwieldy, and they want to use parts >>> of it for JumboClassB, and maybe other things I've introduced them to at >>> that point. If I haven't already, I introduce them to the Math class, show >>> them how it works, let them make a HelloWorld that does something >>> meaningless, let them call it in their existing class, let them call it in >>> another project, let them make a Math2, and then from there, they usually >>> feel comfortable enough to try and extract a method from what they were >>> actually doing into a pure, static function. >>> >>> Are you saying that you only have them write static methods to start >>> with? Like >>> >>> class JumboClassA { >>> static int code() { >>> return JumboClassB.compute() + 1; >>> } >>> } >>> >>> class JumboClassB { >>> static int compute() { >>> return 0; >>> } >>> } >>> >>> If so, do you oft get around to instances and instance state? >>> >>> > Long story short, you and I approach tutoring in WILDLY different ways. >>> >>> Also in different contexts. I spend most of my time in one of a few >>> discord channels answering questions and that sometimes leads to people who >>> I tutor over a longer period of time. We mostly get people who are either >>> struggling with an existing curriculum and try to do course correction (I >>> started making a shortlist of the bad college curriculums we are exposed >>> to). >>> >>> So the things we notice are that current curriculums either >>> * Have ordering issues within them. Explaining JPA before SQL, Abstract >>> classes before interfaces, Large program design before small, etc. >>> * Have no clear next steps. Most intro courses, after taking a deep sigh >>> of relief for having gotten a successful for loop out of a teenager, just >>> sorta end. >>> >>> So we have very few Java courses (that we know of) to point people to >>> that actually explain the topics they introduce in the order they will be >>> encountered. (See https://dev.java/learn/ , >>> https://www.baeldung.com/java-tutorial, >>> https://www.geeksforgeeks.org/java/ for examples of terrible tutorials) >>> >>> And after we give them something like https://java-programming.mooc.fi/ >>> and they finish we have nowhere to go from there. That's part of why I >>> think topic order matters so much. >>> >>> > I have only tutored one student using this implicit class feature so >>> far, and we are nowhere near this point yet. >>> >>> Take notes on how that progresses + any differences from your previous >>> approach. >>> >>> > I guess what I am saying is that, since I don't really depend on >>> static or instance level state, I just don't have this problem. And by the >>> time I do, they have gotten so used to doing functional, that any >>> interaction with global state stands out as something to watch out for. >>> >>> I went to Northeastern and their intro curriculum starts you with Racket >>> doing really rigid comments that drills in sum types + recursion. Then they >>> introduce Java without mutation at all for 80% of the 2nd semester. You >>> only get mutation at the end and at that point your brain was broken from >>> all the purity you forget how. >>> >>> So I get it. But eventually those concepts need to be introduced. >>> >>> >>> On Sun, Jun 2, 2024 at 12:06?AM David Alayachew < >>> davidalayachew at gmail.com> wrote: >>> >>>> Well, I definitely see the discrepancy now. Yes, this makes a lot of >>>> sense. >>>> >>>> Apologies for not recognizing your point earlier, I had assumed that >>>> you were talking about some fundamental execution change that the kid needs >>>> to be aware of. I didn't realize that you were talking about potholes that >>>> the kid might fall into. >>>> >>>> Long story short, you and I approach tutoring in WILDLY different ways. >>>> >>>> First and foremost, the concept of introducing instance state this >>>> early on is dangerous in my eyes. If you have a kid that can dance at that >>>> tempo, then you truly have a gifted kid. But from my 10+ years of tutoring, >>>> EVERY SINGLE TIME I try to introduce them to the idea of having state >>>> ANYWHERE except for inside of a method, things fall apart. It will be a >>>> long time before I ever try and get this kid to try and manage state at the >>>> instance level. >>>> >>>> I approach it very differently. >>>> >>>> I start off teaching these kids to approach problems with a functional >>>> mindset. Pure functions everywhere. This usually ends up creating a big, >>>> singular essay of a function, and once they start to feel the strain from >>>> that, I show them how to thread their state in through methods and return >>>> values. >>>> >>>> Anyways, back to my example from 2 emails ago. >>>> >>>> Their JumboClassA is very big and unwieldy, and they want to use parts >>>> of it for JumboClassB, and maybe other things I've introduced them to at >>>> that point. If I haven't already, I introduce them to the Math class, show >>>> them how it works, let them make a HelloWorld that does something >>>> meaningless, let them call it in their existing class, let them call it in >>>> another project, let them make a Math2, and then from there, they usually >>>> feel comfortable enough to try and extract a method from what they were >>>> actually doing into a pure, static function. >>>> >>>> I have only tutored one student using this implicit class feature so >>>> far, and we are nowhere near this point yet. But I expect that the only >>>> thing that will change before I get there is explaining to them how, in >>>> order to use their methods outside of the class, they need to give the >>>> class an explicit name. I foresee no pain points with that. After all, how >>>> can you reference a class that does not have an explicit name? And I expect >>>> them to understand the unidirectionality of that. A class without a name >>>> can use methods from a class WITH a name. But the reverse is false. I'm >>>> actually sorted of excited to spring that onto the kids and see how they >>>> react lol. >>>> >>>> Really, the only issue I ever run into is when kids ask me, what is >>>> static useful for anyways? They usually feel like they only need static or >>>> instance methods. >>>> >>>> I guess what I am saying is that, since I don't really depend on static >>>> or instance level state, I just don't have this problem. And by the time I >>>> do, they have gotten so used to doing functional, that any interaction with >>>> global state stands out as something to watch out for. From there, they >>>> understand how much of an ordeal it is to work with state, and exactly how >>>> dangerous and error-prone that it is that I can usually get them to turn >>>> their alertness up to 11 whenever the need arises. >>>> >>>> Let me first get your perspective though before I continue my point. >>>> What are your thoughts on this? >>>> >>>> And apologies for not responding to your points individually. Our >>>> assumptions are on opposite ends, so there is very little that I can >>>> directly respond to. >>>> >>>> On Sat, Jun 1, 2024 at 11:22?PM Ethan McCue wrote: >>>> >>>>> I really should find a better example / adapt something in an existing >>>>> curriculum - but: >>>>> >>>>> Mutation 1: Make b static >>>>> >>>>> int count = 0; >>>>> >>>>> class A { >>>>> static void b() { >>>>> count++; >>>>> c(); >>>>> new D(); >>>>> } >>>>> } >>>>> >>>>> void c() { >>>>> println("" + count); >>>>> } >>>>> >>>>> class D {} >>>>> >>>>> void main() { >>>>> new A().b(); >>>>> } >>>>> >>>>> >>>>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=be416fe8e6df84c81c0f5526f0e28aef >>>>> >>>>> Mutation 2: Make c static >>>>> >>>>> int count = 0; >>>>> >>>>> class A { >>>>> void b() { >>>>> count++; >>>>> c(); >>>>> new D(); >>>>> } >>>>> } >>>>> >>>>> static void c() { >>>>> println("" + count); >>>>> } >>>>> >>>>> class D {} >>>>> >>>>> void main() { >>>>> new A().b(); >>>>> } >>>>> >>>>> >>>>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=b71e9b94cff6099afb5153abffdbc3e9 >>>>> >>>>> Mutation 3: (for fun) Make A a record >>>>> >>>>> int count = 0; >>>>> >>>>> record A() { >>>>> void b() { >>>>> count++; >>>>> c(); >>>>> new D(); >>>>> } >>>>> } >>>>> >>>>> void c() { >>>>> println("" + count); >>>>> } >>>>> >>>>> class D {} >>>>> >>>>> void main() { >>>>> new A().b(); >>>>> } >>>>> >>>>> >>>>> https://run.mccue.dev/?runtime=latest&release=22&preview=enabled&gist=cb5bf4664bf83ffc8ee4e5fe85abadb6 >>>>> >>>>> Maybe it's just a failure of imagination, but I don't know how to >>>>> introduce static methods and fields into someone's arsenal without >>>>> either >>>>> * pulling the veil and explaining the anonymous class. This requires >>>>> static imports. We've looped. >>>>> * leaving them with guidance like "you can't make classes, records >>>>> work though. I'll tell you later why. Just do math type stuff here." >>>>> >>>>> Then there is the confounding factor of *why* they would want a static >>>>> method. In "real code" we use static methods and fields for >>>>> >>>>> * globals >>>>> * "pure" logic >>>>> * factory methods >>>>> >>>>> Before they move to another file, they already have globals and they >>>>> can already put pure logic at the top level. Factory methods only make >>>>> sense once you've introduced large program topics like visibility. >>>>> >>>>> I.E. you only need factory methods if >>>>> >>>>> 1. You want to hide a constructor >>>>> >>>>> class Point { >>>>> private Point(int x, int y) {} >>>>> >>>>> static Point of(int x, int y) { >>>>> return new Point(x, y); >>>>> } >>>>> } >>>>> >>>>> But this is a nuanced api topic and isn't even relevant when >>>>> everything is a nestmate. Really a "large program" sort of choice. >>>>> >>>>> 2. You want to create through an interface >>>>> >>>>> interface LinkedList { >>>>> int size(); >>>>> >>>>> static LinkedList empty() { >>>>> return new EmptyList(); >>>>> } >>>>> >>>>> static LinkedList notEmpty(int head, LinkedList tail) { >>>>> >>>>> } >>>>> } >>>>> >>>>> class EmptyList implements LinkedList { >>>>> @Override >>>>> public int size() { >>>>> return 0; >>>>> } >>>>> } >>>>> >>>>> But as you see from the code you need to have public to teach >>>>> interfaces. You can gloss over it (my school did) but it's there. Even then >>>>> "why not just new EmptyList()" has an answer really rooted in (again) large >>>>> program concerns. You make code for potentially an external audience and >>>>> need to give a "good" API. >>>>> >>>>> 3. You want otherwise incompatible overloads >>>>> >>>>> class Point { >>>>> Point(int x, int y) {} >>>>> >>>>> static Point ofX(int x) { >>>>> return new Point(x, 0); >>>>> } >>>>> >>>>> static Point ofY(int y) { >>>>> return new Point(0, y); >>>>> } >>>>> } >>>>> >>>>> Which does work a little. But it doesn't really give a good opener for >>>>> static imports. The method names you'd give here are like ofX, fromX. You >>>>> would want to use those qualified every time. You also activate the >>>>> footguns I showed above. >>>>> >>>>> int pointsMade = 0; >>>>> >>>>> class Point { >>>>> Point(int x, int y) {} >>>>> >>>>> static Point ofX(int x) { >>>>> pointsMade++; >>>>> return new Point(x, 0); >>>>> } >>>>> >>>>> static Point ofY(int y) { >>>>> pointsMade++; >>>>> return new Point(0, y); >>>>> } >>>>> } >>>>> >>>>> -- >>>>> >>>>> > I would always start by taking one of their projects, ask them to >>>>> tweak in a subtle, but difficult way, then let them run headfirst into the >>>>> problem. Once they start to feel the strain, I would introduce them to the >>>>> concepts of classes. >>>>> >>>>> I think this is what I am missing. Without going into large or >>>>> multi-file program design there isn't much reason to use static anything. *In >>>>> addition* to that there are mechanical difficulties that can only be >>>>> explained properly once you reach a 2nd file. >>>>> >>>>> I.E. - "you were actually using a class until now. For true globals >>>>> use static fields", "For methods you can call anywhere use static" are the >>>>> explanations I want to give. Both of those explanations need multiple files >>>>> already in place to make sense. >>>>> >>>>> Does that track? >>>>> >>>>> >>>>> >>>>> On Sat, Jun 1, 2024 at 10:47?PM David Alayachew < >>>>> davidalayachew at gmail.com> wrote: >>>>> >>>>>> >>>>>> Heh, we're both unpaid :P I tutor folks from my university and >>>>>> church, as well as a couple of friends and family. Slowing down now though >>>>>> because of workload at work. >>>>>> >>>>>> But back to the point. >>>>>> >>>>>> > I think the problem is that without static "global" >>>>>> > fields are always accessible and all the nested classes >>>>>> > in their program are instansable from anywhere. >>>>>> > >>>>>> > Once you have a static method, a method on a record, a >>>>>> > method on an enum, or a static inner class this is no >>>>>> > longer the case. >>>>>> >>>>>> Apologies, maybe I missed the memo. I have been out of touch with >>>>>> this (and all other JEP's) because of my insane workload for the past >>>>>> couple of months. >>>>>> >>>>>> Why would adding a static method change any of the logic? >>>>>> >>>>>> I took your example in Java 22, added a static method, and everything >>>>>> worked dandy. >>>>>> >>>>>> Am I missing something? >>>>>> >>>>>> On Sat, Jun 1, 2024 at 9:28?PM Ethan McCue wrote: >>>>>> >>>>>>> I am also basically just a tutor, just unpaid. >>>>>>> >>>>>>> I think the problem is that without static "global" fields are >>>>>>> always accessible and all the nested classes in their program are >>>>>>> instansable from anywhere. >>>>>>> >>>>>>> Once you have a static method, a method on a record, a method on an >>>>>>> enum, or a static inner class this is no longer the case. >>>>>>> >>>>>>> Trite example of the mechanics I'm referencing: >>>>>>> >>>>>>> int count = 0; >>>>>>> >>>>>>> class A { >>>>>>> void b() { >>>>>>> count++; >>>>>>> c(); >>>>>>> new D(); >>>>>>> } >>>>>>> } >>>>>>> >>>>>>> void c() { >>>>>>> println("" + count); >>>>>>> } >>>>>>> >>>>>>> class D {} >>>>>>> >>>>>>> void main() { >>>>>>> new A().b(); >>>>>>> } >>>>>>> >>>>>>> So when you show static methods, I think you need to understand why >>>>>>> count++; and c(); and new D(); would not function. >>>>>>> >>>>>>> That requires knowing that you are actually in an anonymous class. >>>>>>> That requires we get past import static java.io.IO. That's the loop. >>>>>>> >>>>>>> >>>>>>> >>>>>>> On Sat, Jun 1, 2024, 8:59 PM David Alayachew < >>>>>>> davidalayachew at gmail.com> wrote: >>>>>>> >>>>>>>> Maybe I am missing something, but a static import seems like the >>>>>>>> natural next step to teaching a student who made it that far. >>>>>>>> >>>>>>>> If a student feels the need to break out of the bounds of an >>>>>>>> implicitly declared class, they must first understand what a class is going >>>>>>>> to save them from. In this case, it is allowing code they right to be >>>>>>>> reused else where. >>>>>>>> >>>>>>>> I am only a tutor, not a full-blown teacher, but when tutoring >>>>>>>> folks in this exact subject, I would always start by taking one of their >>>>>>>> projects, ask them to tweak in a subtle, but difficult way, then let them >>>>>>>> run headfirst into the problem. Once they start to feel the strain, I would >>>>>>>> introduce them to the concepts of classes. >>>>>>>> >>>>>>>> I would start by showing them how to create a simple pure function, >>>>>>>> similar to java.lang.Math. From there, we would make a few pure functions >>>>>>>> that are unrelated to their current task, and then have them get >>>>>>>> comfortable with that concept. >>>>>>>> >>>>>>>> Then, we would make another pure function that is being done >>>>>>>> repeatedly in their "JumboClass1" and "JumboClass2", and then have them get >>>>>>>> comfortable using this helper method in both versions. >>>>>>>> >>>>>>>> In my mind, this is naturally where the student would discover that >>>>>>>> they need to do a static import. In which case, they are in the middle of >>>>>>>> understanding a class, they understand imports, and they understand static >>>>>>>> methods fairly well. So, a static import has been well prepared for them. >>>>>>>> >>>>>>>> For me, this is a neat and clean introduction. Maybe I am missing >>>>>>>> something? >>>>>>>> >>>>>>>> On Sat, Jun 1, 2024 at 6:43?PM Ron Pressler < >>>>>>>> ron.pressler at oracle.com> wrote: >>>>>>>> >>>>>>>>> Hi. >>>>>>>>> >>>>>>>>> Without getting into the merits of not implicitly importing >>>>>>>>> anything, let me just point out that the following is a valid program under >>>>>>>>> the current JEP: >>>>>>>>> >>>>>>>>> void main() { >>>>>>>>> IO.println(?Hello, world!?); >>>>>>>>> } >>>>>>>>> >>>>>>>>> As is this one: >>>>>>>>> >>>>>>>>> import module java.base; >>>>>>>>> >>>>>>>>> void main() { >>>>>>>>> IO.println(?Hello, world!?); >>>>>>>>> } >>>>>>>>> >>>>>>>>> In other words, the fact that there are implicit imports doesn?t >>>>>>>>> mean that you can?t ignore them or add them explicitly. if you find >>>>>>>>> teaching this to be easier, you can. So even if you don?t find implicit >>>>>>>>> imports helpful, they may be helpful to other teachers, who may not want to >>>>>>>>> start with some import incantation that necessarily implies some >>>>>>>>> programming-in-the-large concept. >>>>>>>>> >>>>>>>>> But I think that your unease mostly stems from the extra magic >>>>>>>>> that implicit classes enjoy, and which isn?t strictly necessitated by their >>>>>>>>> primary quality of being implicitly declared classes, and that extra magic >>>>>>>>> differentiates them from regular compilation units along some other axis; >>>>>>>>> is that right? >>>>>>>>> >>>>>>>>> ? Ron >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> > On 1 Jun 2024, at 23:14, Ethan McCue >>>>>>>>> wrote: >>>>>>>>> > >>>>>>>>> > Hi all, >>>>>>>>> > >>>>>>>>> > I'm following the development of JEP 477[1] and I feel the need >>>>>>>>> to question the impetus for the implicit static imports. >>>>>>>>> > >>>>>>>>> > As of now[2] any program like this >>>>>>>>> > >>>>>>>>> > void main() { >>>>>>>>> > println("Hello, world"); >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > Is equivalent to >>>>>>>>> > >>>>>>>>> > import static java.io.IO.print; >>>>>>>>> > import static java.io.IO.println; >>>>>>>>> > import static java.io.IO.writeln; >>>>>>>>> > >>>>>>>>> > import module java.base; >>>>>>>>> > >>>>>>>>> > final class Main { >>>>>>>>> > void main() { >>>>>>>>> > println("Hello, world"); >>>>>>>>> > } >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > Where all the methods in java.io.IO delegate to newly added >>>>>>>>> equivalent methods in java.io.Console.[3] >>>>>>>>> > >>>>>>>>> > Aside from muddying that API up (now there is readln and >>>>>>>>> readLine + println and printLine which...what) I'm still concerned on how >>>>>>>>> those implicit imports will affect the transition to named classes. >>>>>>>>> > >>>>>>>>> > Assume we start a student out here >>>>>>>>> > >>>>>>>>> > void main() { >>>>>>>>> > println("Hello, world"); >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > You can get through conditionals, loops, variables, methods, and >>>>>>>>> return types before touching classes or access specifiers. >>>>>>>>> > >>>>>>>>> > int compute() { >>>>>>>>> > int total = 0; >>>>>>>>> > for (int i = 0; i < 10; i++) { >>>>>>>>> > total += i; >>>>>>>>> > } >>>>>>>>> > return total; >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > void main() { >>>>>>>>> > println("Hello: " + compute()); >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > You can even talk about records and enums in a hand wavey way. >>>>>>>>> Enums are "one of these options", Records are "if you want to return two >>>>>>>>> things." >>>>>>>>> > >>>>>>>>> > enum Pirate { >>>>>>>>> > BLACKBEARD, >>>>>>>>> > OTHER >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > record Pos(int x, int y) {} >>>>>>>>> > >>>>>>>>> > Pos treasure(Pirate pirate) { >>>>>>>>> > switch (pirate) { >>>>>>>>> > case BLACKBEARD -> >>>>>>>>> > return new Pos(5, 5); >>>>>>>>> > case OTHER -> >>>>>>>>> > return new Pos(0, 0); >>>>>>>>> > } >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > void main() { >>>>>>>>> > println(treasure(Pirate.OTHER)); >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > So it is reasonable for a student to have made a relatively >>>>>>>>> complex program before having to get to that point, but I think you do need >>>>>>>>> to explain what exactly is going on with the anonymous main class when you >>>>>>>>> introduce multi file programs. >>>>>>>>> > >>>>>>>>> > As originally pitched, this transition would have just meant >>>>>>>>> wrapping the whole program in class Main {}, but now to make the transition >>>>>>>>> from >>>>>>>>> > >>>>>>>>> > void main() { >>>>>>>>> > // Arbitrary code >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > to >>>>>>>>> > >>>>>>>>> > class Main { >>>>>>>>> > void main() { >>>>>>>>> > // Arbitrary code >>>>>>>>> > } >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > In a robust way, you need to add those imports to the top. You >>>>>>>>> can skip the final since the semantics of extension haven't been relevant >>>>>>>>> yet. >>>>>>>>> > >>>>>>>>> > import static java.io.IO.*; >>>>>>>>> > >>>>>>>>> > import module java.base; >>>>>>>>> > >>>>>>>>> > class Main { >>>>>>>>> > void main() { >>>>>>>>> > // Arbitrary code >>>>>>>>> > } >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > My gripe is that the concepts introduced here - static, module, >>>>>>>>> and * imports - would have had no place to be introduced earlier. >>>>>>>>> > >>>>>>>>> > If you have folks write static methods on inner classes, the >>>>>>>>> metaphor of a "global field" that otherwise exists in the simple-main world >>>>>>>>> goes away. >>>>>>>>> > >>>>>>>>> > // For every program up until this, they could freely access >>>>>>>>> this from anywhere >>>>>>>>> > int count = 0; >>>>>>>>> > >>>>>>>>> > // And they could freely make an instance of any inner class >>>>>>>>> > class Other {} >>>>>>>>> > >>>>>>>>> > class Pos { >>>>>>>>> > static Pos of(int x, int y) { >>>>>>>>> > // So the rules they are used to don't apply anymore >>>>>>>>> > // and the explanation as to why really lies *after* >>>>>>>>> they understand >>>>>>>>> > // what an anonymous main class is and does >>>>>>>>> > // ... >>>>>>>>> > } >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > void main() { >>>>>>>>> > // ... >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > If you have folks *use* static methods that is fine - the >>>>>>>>> hand-waving of Math.max doesn't seem to trip anyone up - but I can't figure >>>>>>>>> out how to topologically sort topics such that import static makes any >>>>>>>>> sense before going through multi-file programs. >>>>>>>>> > >>>>>>>>> > I have a similar concern with import module, but that can be >>>>>>>>> hand waved as "gets you everything from this library" so I am less >>>>>>>>> concerned. Still don't fully understand the desire to have it be implicit, >>>>>>>>> but less concerned. >>>>>>>>> > >>>>>>>>> > Just as a hypothetical, say java.io.IO was java.lang.IO. No new >>>>>>>>> import rules needed and now IO is available to all programs. >>>>>>>>> > >>>>>>>>> > void main() { >>>>>>>>> > IO.printLine("Hello, world"); >>>>>>>>> > } >>>>>>>>> > >>>>>>>>> > While this does introduce something not explained - static >>>>>>>>> method access - it feels easier to hand-wave away than import static >>>>>>>>> java.io.IO.*; would be. It calls the printLine method, it comes from IO. IO >>>>>>>>> means "Input/Output". That's a workable metaphor and can be glanced over in >>>>>>>>> the same way as Math.max >>>>>>>>> > >>>>>>>>> > The transition from here to classes can again be "it's the same >>>>>>>>> as if you had class Main {} around it" instead of "it's the same as if you >>>>>>>>> had class Main {} around it and these imports. A static import imports a >>>>>>>>> static method. A static method is a method attached to the class itself >>>>>>>>> instead of the instance.... etc." >>>>>>>>> > >>>>>>>>> > Also, IO. feels like a different kind of unexplained boilerplate >>>>>>>>> than public static void main(String[] args) {}. There is a lot of ground >>>>>>>>> you need to cover before access modifiers, static methods, or command line >>>>>>>>> arguments make any sort of sense. When you have someone write IO they are >>>>>>>>> in that moment producing output and will soon take input. >>>>>>>>> > >>>>>>>>> > What am I overlooking? >>>>>>>>> > >>>>>>>>> > >>>>>>>>> > [1]: https://openjdk.org/jeps/477 >>>>>>>>> > [2]: >>>>>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/IO.html >>>>>>>>> > [3] >>>>>>>>> https://download.java.net/java/early_access/jdk23/docs/api/java.base/java/io/Console.html#print(java.lang.Object) >>>>>>>>> >>>>>>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Mon Jun 3 14:00:05 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Mon, 3 Jun 2024 17:00:05 +0300 Subject: Some thoughts on Member Patterns as parser developer In-Reply-To: <08f2bf28-02b0-4b3d-95fe-0dd562abc849@oracle.com> References: <6bbee91d-6a56-4a4a-8bff-0fbe7a3b4c67@oracle.com> <08f2bf28-02b0-4b3d-95fe-0dd562abc849@oracle.com> Message-ID: I have been recently looking through devox speeches, and one of the points you have made reminded me about this conversation. The thing you said is something like "if we had pattern matching back in java 8, we could have implemented the following for the CompletableFuture" and showed an example of switching over possible states (like interrupted, completed etc). And what has crossed my mind is that this is almost what I have tried to express. If I understand correctly, member patterns are a way to say "I declare a, possibly exhaustive, set of states that class that patterns are members of could take". This is a good thing obviously, but this only works if one has access to source code of the class one wants to declare states of. What am I asking for / suggesting, is to also add the possibility to declare such set of states for external classes. More formally, I want to be able to state something like "I declare a set of states X that type Y could take in the subject area Z, that could be exhaustive if asserted". Using an example with tokes, I could reformulate this as follows: "I declare a set of states TokenKinds that type String could take in the subject area "Tokens", that is exhaustive by assertion". Subject area is not something present in language semantics, more of a topic that unites this set of states. Applying to CompletableFuture, this feature could introduce something like: switch (future) over AsyncStates { case Completed(var result) -> ... case Interrupted -> ... case Failed(var ex) -> ... } Note that "over AsyncStates" asserts that only patterns from set of states AsyncStates could be present This could help adapt existing APIs to new java paradigms and features without having to modify them directly, which is in many cases at least unpleasant. Also, this opens a world of possibilities for users, by enabling them to use custom patterns on types they cant access source code of. Not limited to the Java standard library, It could be also useful in frameworks, with tasks like checking if principal is anonymous/logged in/unauthorized, and a huge pile of other ways that people could apply this feature. On Mon, Apr 29, 2024 at 6:27?PM Brian Goetz wrote: > > > On 4/29/2024 10:02 AM, Olexandr Rotan wrote: > > I think I did a really poor job expressing my thoughts in the first > > message. I will try to be more clear now, along with some situations I > > have encountered myself. > > > > Assume we have a stateless math expressions parser. For simplicity, > > let's assume that we split expressions into several types: > > "computation-heavy", "computation-lightweight", "erroneous" (permits > > null as input) and "computation-remote" (delegate parsing to another > > service via some communication protocol), and types can be assigned > > heuristically (speculatively). For some tasks, we need to perform some > > pre- and postprocessing around core parsing logic, like result > > caching, wrapping parsing into virtual thread and registering it in > > phaser etc., for others - log warning or error, or fail parsing with > > exception. > > If you are envisioning side-effects, then I think you are already > abusing patterns. Patterns either match, or they don't, and if they do, > they may produce bindings to describe witnesses to the match. Exceptions > are not available to you as a normal means of "something went wrong"; in > writing patterns (you should think of throwing an exception from a > pattern declaration as being only a few percent less drastic than > calling System.exit()). > > Patterns, as explained in the various writeups, are the dual > (deconstruction) of certain methods (aggregations.) I don't see the > duality here. (If I had to guess, you're trying to bootstrap your way > to parser combinators with patterns, but that's a different feature.) > So I'm still not sure that you're using patterns right, so I still want > to focus on that before we start inventing new features. > > Case patterns are a form of ad-hoc exhaustiveness, to be used only when > other sources of exhaustiveness (e.g., enums, sealed types, ADTs) fail. > It isn't clear to me yet that these other sources have failed. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Mon Jun 3 14:27:20 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Mon, 3 Jun 2024 17:27:20 +0300 Subject: Some thoughts on Member Patterns as parser developer In-Reply-To: References: <6bbee91d-6a56-4a4a-8bff-0fbe7a3b4c67@oracle.com> <08f2bf28-02b0-4b3d-95fe-0dd562abc849@oracle.com> Message-ID: Sorry to send this as follow up, but I figured there are some points that have been left unaddressed. Firstly, I am aware that patterns are dual of factories. There is no duality in what I am asking for (at least it can be used without duality), although it can still be used as dual (as token patterns could be dual to corresponding token type factories, in my example). Secondly, if we are talking about extending standard library APIs, as for every other extension (collectors, for example), there would be some lack of discoverability. Not that it makes it a bad way to extend existing code, just think it is worth pointing out. Lastly, the example I have provided, which is obvious for me now, does not make much sense. THe thing I want to propose now, if applied to example above, should look something like this^ switch (strToken) over TokenKinds { case matchesHeavy(HeavyToken t) -> ... case matchesErroneouse(ErroneousToken t) -> ... ... } This example makes much more sense then what I have shown above after researching how patterns are intended to work. Not even sure what I was trying to express above to be honest :) On Mon, Jun 3, 2024 at 5:00?PM Olexandr Rotan wrote: > I have been recently looking through devox speeches, and one of the points > you have made reminded me about this conversation. The thing you said is > something like "if we had pattern matching back in java 8, we could have > implemented the following for the CompletableFuture" and showed an example > of switching over possible states (like interrupted, completed etc). And > what has crossed my mind is that this is almost what I have tried to > express. > > If I understand correctly, member patterns are a way to say "I declare a, > possibly exhaustive, set of states that class that patterns are members of > could take". This is a good thing obviously, but this only works if one has > access to source code of the class one wants to declare states of. > > What am I asking for / suggesting, is to also add the possibility to > declare such set of states for external classes. More formally, I want to > be able to state something like "I declare a set of states X that type Y > could take in the subject area Z, that could be exhaustive if asserted". > Using an example with tokes, I could reformulate this as follows: "I > declare a set of states TokenKinds that type String could take in the > subject area "Tokens", that is exhaustive by assertion". Subject area is > not something present in language semantics, more of a topic that unites > this set of states. Applying to CompletableFuture, this feature could > introduce something like: > > switch (future) over AsyncStates { > case Completed(var result) -> ... > case Interrupted -> ... > case Failed(var ex) -> ... > } > > Note that "over AsyncStates" asserts that only patterns from set of states > AsyncStates could be present > > This could help adapt existing APIs to new java paradigms and features > without having to modify them directly, which is in many cases at least > unpleasant. Also, this opens a world of possibilities for users, by > enabling them to use custom patterns on types they cant access source code > of. Not limited to the Java standard library, It could be also useful in > frameworks, with tasks like checking if principal is anonymous/logged > in/unauthorized, and a huge pile of other ways that people could apply this > feature. > > On Mon, Apr 29, 2024 at 6:27?PM Brian Goetz > wrote: > >> >> >> On 4/29/2024 10:02 AM, Olexandr Rotan wrote: >> > I think I did a really poor job expressing my thoughts in the first >> > message. I will try to be more clear now, along with some situations I >> > have encountered myself. >> > >> > Assume we have a stateless math expressions parser. For simplicity, >> > let's assume that we split expressions into several types: >> > "computation-heavy", "computation-lightweight", "erroneous" (permits >> > null as input) and "computation-remote" (delegate parsing to another >> > service via some communication protocol), and types can be assigned >> > heuristically (speculatively). For some tasks, we need to perform some >> > pre- and postprocessing around core parsing logic, like result >> > caching, wrapping parsing into virtual thread and registering it in >> > phaser etc., for others - log warning or error, or fail parsing with >> > exception. >> >> If you are envisioning side-effects, then I think you are already >> abusing patterns. Patterns either match, or they don't, and if they do, >> they may produce bindings to describe witnesses to the match. Exceptions >> are not available to you as a normal means of "something went wrong"; in >> writing patterns (you should think of throwing an exception from a >> pattern declaration as being only a few percent less drastic than >> calling System.exit()). >> >> Patterns, as explained in the various writeups, are the dual >> (deconstruction) of certain methods (aggregations.) I don't see the >> duality here. (If I had to guess, you're trying to bootstrap your way >> to parser combinators with patterns, but that's a different feature.) >> So I'm still not sure that you're using patterns right, so I still want >> to focus on that before we start inventing new features. >> >> Case patterns are a form of ad-hoc exhaustiveness, to be used only when >> other sources of exhaustiveness (e.g., enums, sealed types, ADTs) fail. >> It isn't clear to me yet that these other sources have failed. >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From mataha at protonmail.com Mon Jun 3 14:46:23 2024 From: mataha at protonmail.com (Mateusz Kazimierczuk) Date: Mon, 03 Jun 2024 14:46:23 +0000 Subject: error: bad file name on chimera scripts using JEP 463 Message-ID: Hello, I originally raised this on adoptium-support issue tracker, but was advised to post here instead (https://github.com/adoptium/adoptium-support/issues/1106). Chimera scripts that take advantage of JEP 463 return error: bad file name when executed with the source code launcher. Tested with 22.0.1 on Windows and Linux, but I would imagine it's the same on other platforms. amber.cmd: @/*/ '"' 2>/dev/null || true #" 2>nul || setlocal && echo off && goto :cmd.exe exec java --source 22 --enable-preview "${0}" "${@}" exit :cmd.exe (for /f delims^= %%c in (^""%ComSpec%"^") do (goto) 2>nul || title^ %%~c) ^ && java --source 22 --enable-preview "%~dpnx0" %* goto :EOF @/*/SafeVarargs static void main(String... args) { System.out.println("Write once, run anywhere"); } Windows: .\amber.cmd Linux: ./amber.cmd Expected result: Write once, run anywhere Actual result: C:\Users\user\amber.cmd:1: error: bad file name: amber.cmd @/*/ '"' 2>/dev/null || true #" 2>nul || setlocal && echo off && goto :cmd.exe ^ 1 error error: compilation failed I don't believe the script itself is at fault - the following compiles normally: @/*/ '"' 2>/dev/null || true #" 2>nul || setlocal && echo off && goto :cmd.exe exec java --source 22 --enable-preview "${0}" "${@}" exit :cmd.exe (for /f delims^= %%c in (^""%ComSpec%"^") do (goto) 2>nul || title^ %%~c) ^ && java --source 22 --enable-preview "%~dpnx0" %* goto :EOF @/*/SuppressWarnings("unused") public class Main { public static void main(String... args) { System.out.println("Write once, run anywhere"); } } $ .\amber.cmd Write once, run anywhere Best regards, Mateusz Kazimierczuk (By the way - I can't express how excited I am for JEP 477. The nature of these scripts makes adding import statements impossible, as a file has to start with "@/*/", at which point it's too late to import anything - only fully-qualified names work. Implicit java.base import removes much of the verbosity. Thanks!) From brian.goetz at oracle.com Mon Jun 3 16:20:08 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 3 Jun 2024 16:20:08 +0000 Subject: Some thoughts on Member Patterns as parser developer In-Reply-To: References: <6bbee91d-6a56-4a4a-8bff-0fbe7a3b4c67@oracle.com> <08f2bf28-02b0-4b3d-95fe-0dd562abc849@oracle.com> Message-ID: <95732197-6DFA-431C-9F83-19A304309040@oracle.com> If I understand correctly, member patterns are a way to say "I declare a, possibly exhaustive, set of states that class that patterns are members of could take". This is a good thing obviously, but this only works if one has access to source code of the class one wants to declare states of. To be more precise, using your terminology: - A _deconstruction pattern_ declares a state that all instances of the current class must be in. (This derives from deconstruction patterns being total.) - An _instance pattern_ declares a state that some instances of the current class can be in. Under the proposed ?case pattern? approach, a set of such patterns can indicate that they cover all instances of the class. - A _static pattern_ declares a state that some instances of _some other_ class can be in. Same story about ?case pattern?, though it is far more likely people will make incorrect assumptions / implementations about exhaustiveness when they are working at arms length. So I believe that what you want is handled by static patterns, optionally augmented with some mechanism for exhaustiveness. I suspect that you are more interested in the exhaustiveness part than the static pattern part, but I think there are diminishing returns there. Any statement of ?this set of patterns is exhaustive? is not trustable, so the compiler always has to insert a synthetic default anyway. What am I asking for / suggesting, is to also add the possibility to declare such set of states for external classes. More formally, I want to be able to state something like "I declare a set of states X that type Y could take in the subject area Z, that could be exhaustive if asserted". Using an example with tokes, I could reformulate this as follows: "I declare a set of states TokenKinds that type String could take in the subject area "Tokens", that is exhaustive by assertion". Subject area is not something present in language semantics, more of a topic that unites this set of states. Applying to CompletableFuture, this feature could introduce something like: switch (future) over AsyncStates { case Completed(var result) -> ... case Interrupted -> ... case Failed(var ex) -> ... } Note that "over AsyncStates" asserts that only patterns from set of states AsyncStates could be present This could help adapt existing APIs to new java paradigms and features without having to modify them directly, which is in many cases at least unpleasant. Also, this opens a world of possibilities for users, by enabling them to use custom patterns on types they cant access source code of. Not limited to the Java standard library, It could be also useful in frameworks, with tasks like checking if principal is anonymous/logged in/unauthorized, and a huge pile of other ways that people could apply this feature. On Mon, Apr 29, 2024 at 6:27?PM Brian Goetz > wrote: On 4/29/2024 10:02 AM, Olexandr Rotan wrote: > I think I did a really poor job expressing my thoughts in the first > message. I will try to be more clear now, along with some situations I > have encountered myself. > > Assume we have a stateless math expressions parser. For simplicity, > let's assume that we split expressions into several types: > "computation-heavy", "computation-lightweight", "erroneous" (permits > null as input) and "computation-remote" (delegate parsing to another > service via some communication protocol), and types can be assigned > heuristically (speculatively). For some tasks, we need to perform some > pre- and postprocessing around core parsing logic, like result > caching, wrapping parsing into virtual thread and registering it in > phaser etc., for others - log warning or error, or fail parsing with > exception. If you are envisioning side-effects, then I think you are already abusing patterns. Patterns either match, or they don't, and if they do, they may produce bindings to describe witnesses to the match. Exceptions are not available to you as a normal means of "something went wrong"; in writing patterns (you should think of throwing an exception from a pattern declaration as being only a few percent less drastic than calling System.exit()). Patterns, as explained in the various writeups, are the dual (deconstruction) of certain methods (aggregations.) I don't see the duality here. (If I had to guess, you're trying to bootstrap your way to parser combinators with patterns, but that's a different feature.) So I'm still not sure that you're using patterns right, so I still want to focus on that before we start inventing new features. Case patterns are a form of ad-hoc exhaustiveness, to be used only when other sources of exhaustiveness (e.g., enums, sealed types, ADTs) fail. It isn't clear to me yet that these other sources have failed. -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Mon Jun 3 17:30:46 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Mon, 3 Jun 2024 20:30:46 +0300 Subject: Some thoughts on Member Patterns as parser developer In-Reply-To: <95732197-6DFA-431C-9F83-19A304309040@oracle.com> References: <6bbee91d-6a56-4a4a-8bff-0fbe7a3b4c67@oracle.com> <08f2bf28-02b0-4b3d-95fe-0dd562abc849@oracle.com> <95732197-6DFA-431C-9F83-19A304309040@oracle.com> Message-ID: Yes, I am in fact talking about exhaustiveness (and possibly patterns hierarchy that I have talked about initially). The "set of states" is just a nominal aggregation of such patterns. I believe that in most cases, patterns are aggregated by some feature (state of async op, state of stream, empty/non-empty optional etc), this is just a possible way to let the language know this information too for some purpose. I do believe that syntax like switch (...) over SetOfStates is good for readability in case there are many groups of patterns for the same type as the code. Also, it allows to isolate group of patterns that represent subject area one is switching over from any other patterns, wo there are no situations, where programmer could think "Well, I checked if it matches 3 patterns from area A, now lets see if it matches something from area B", which is kind of mixing apples and hippos and is simply incorrect way to write code. Now to the elephant in the room: exhaustiveness. I understand your concerns about a whole world of places where error can occur, if you just let users assert that set of states is exhaustive. On the other hand, I think you also understand how much value this could bring for readability, code structuring and exhaustiveness. So my opinion on whether the option to assert exhaustiveness should exist or not: I really don't know. There could be some herbivore compromises like throwing IllegalStateException in the synthetic default branch, which both fits semantically and kind of solves the problem, but this is still a compromise. This is kind of a double-edged blade here and having things like that is not in Java's fashion. Although,throwing an exception in case of incorrect assertion is too high a price to pay to discard this feature completely, sacrificing enhanced readability, structuring and exhaustiveness? I think this is up to you as language designers to decide. I, as a regular user, would say that I would be willing to pay that price if I had the option to. It is also a unique feature to have, a selling point of Java in some sense. Also, maybe it is just me, but this switch over syntax seems just really neat and being really descriptive about what this switch statement/expression is responsible for. On Mon, Jun 3, 2024 at 7:20?PM Brian Goetz wrote: > > > If I understand correctly, member patterns are a way to say "I declare a, > possibly exhaustive, set of states that class that patterns are members of > could take". This is a good thing obviously, but this only works if one has > access to source code of the class one wants to declare states of. > > > To be more precise, using your terminology: > > - A _deconstruction pattern_ declares a state that all instances of the > current class must be in. (This derives from deconstruction patterns being > total.) > - An _instance pattern_ declares a state that some instances of the > current class can be in. Under the proposed ?case pattern? approach, a set > of such patterns can indicate that they cover all instances of the class. > - A _static pattern_ declares a state that some instances of _some other_ > class can be in. Same story about ?case pattern?, though it is far more > likely people will make incorrect assumptions / implementations about > exhaustiveness when they are working at arms length. > > So I believe that what you want is handled by static patterns, optionally > augmented with some mechanism for exhaustiveness. > > I suspect that you are more interested in the exhaustiveness part than the > static pattern part, but I think there are diminishing returns there. Any > statement of ?this set of patterns is exhaustive? is not trustable, so the > compiler always has to insert a synthetic default anyway. > > > What am I asking for / suggesting, is to also add the possibility to > declare such set of states for external classes. More formally, I want to > be able to state something like "I declare a set of states X that type Y > could take in the subject area Z, that could be exhaustive if asserted". > Using an example with tokes, I could reformulate this as follows: "I > declare a set of states TokenKinds that type String could take in the > subject area "Tokens", that is exhaustive by assertion". Subject area is > not something present in language semantics, more of a topic that unites > this set of states. Applying to CompletableFuture, this feature could > introduce something like: > > switch (future) over AsyncStates { > case Completed(var result) -> ... > case Interrupted -> ... > case Failed(var ex) -> ... > } > > Note that "over AsyncStates" asserts that only patterns from set of states > AsyncStates could be present > > This could help adapt existing APIs to new java paradigms and features > without having to modify them directly, which is in many cases at least > unpleasant. Also, this opens a world of possibilities for users, by > enabling them to use custom patterns on types they cant access source code > of. Not limited to the Java standard library, It could be also useful in > frameworks, with tasks like checking if principal is anonymous/logged > in/unauthorized, and a huge pile of other ways that people could apply this > feature. > > On Mon, Apr 29, 2024 at 6:27?PM Brian Goetz > wrote: > >> >> >> On 4/29/2024 10:02 AM, Olexandr Rotan wrote: >> > I think I did a really poor job expressing my thoughts in the first >> > message. I will try to be more clear now, along with some situations I >> > have encountered myself. >> > >> > Assume we have a stateless math expressions parser. For simplicity, >> > let's assume that we split expressions into several types: >> > "computation-heavy", "computation-lightweight", "erroneous" (permits >> > null as input) and "computation-remote" (delegate parsing to another >> > service via some communication protocol), and types can be assigned >> > heuristically (speculatively). For some tasks, we need to perform some >> > pre- and postprocessing around core parsing logic, like result >> > caching, wrapping parsing into virtual thread and registering it in >> > phaser etc., for others - log warning or error, or fail parsing with >> > exception. >> >> If you are envisioning side-effects, then I think you are already >> abusing patterns. Patterns either match, or they don't, and if they do, >> they may produce bindings to describe witnesses to the match. Exceptions >> are not available to you as a normal means of "something went wrong"; in >> writing patterns (you should think of throwing an exception from a >> pattern declaration as being only a few percent less drastic than >> calling System.exit()). >> >> Patterns, as explained in the various writeups, are the dual >> (deconstruction) of certain methods (aggregations.) I don't see the >> duality here. (If I had to guess, you're trying to bootstrap your way >> to parser combinators with patterns, but that's a different feature.) >> So I'm still not sure that you're using patterns right, so I still want >> to focus on that before we start inventing new features. >> >> Case patterns are a form of ad-hoc exhaustiveness, to be used only when >> other sources of exhaustiveness (e.g., enums, sealed types, ADTs) fail. >> It isn't clear to me yet that these other sources have failed. >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Jun 3 17:35:30 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 3 Jun 2024 17:35:30 +0000 Subject: Some thoughts on Member Patterns as parser developer In-Reply-To: References: <6bbee91d-6a56-4a4a-8bff-0fbe7a3b4c67@oracle.com> <08f2bf28-02b0-4b3d-95fe-0dd562abc849@oracle.com> <95732197-6DFA-431C-9F83-19A304309040@oracle.com> Message-ID: <6399529F-6CD2-4508-86AD-7399B579F2F5@oracle.com> Oh, I understand where you are coming from. Being able to capture completion assertions, and have them be type checked at compile time (and backed up by runtime tests at run time, if necessary) is a ?happy clean? feeling. Which is why we see it as a ?stretch? goal for member patterns. But we are also mindful of the diminishing returns, where the incremental benefit falls short of the incremental complexity. So this is still up in the air. Note that we already do throw MatchException when a synthetic default is triggered. This can happen when you have an enum or sealed class and it acquires another constant/subtype, and the code with the switch is not recompiled. Then the synthetic default becomes reachable, and to avoid hazards like switch expressions not having a value, ME is thrown. On Jun 3, 2024, at 1:30 PM, Olexandr Rotan > wrote: Yes, I am in fact talking about exhaustiveness (and possibly patterns hierarchy that I have talked about initially). The "set of states" is just a nominal aggregation of such patterns. I believe that in most cases, patterns are aggregated by some feature (state of async op, state of stream, empty/non-empty optional etc), this is just a possible way to let the language know this information too for some purpose. I do believe that syntax like switch (...) over SetOfStates is good for readability in case there are many groups of patterns for the same type as the code. Also, it allows to isolate group of patterns that represent subject area one is switching over from any other patterns, wo there are no situations, where programmer could think "Well, I checked if it matches 3 patterns from area A, now lets see if it matches something from area B", which is kind of mixing apples and hippos and is simply incorrect way to write code. Now to the elephant in the room: exhaustiveness. I understand your concerns about a whole world of places where error can occur, if you just let users assert that set of states is exhaustive. On the other hand, I think you also understand how much value this could bring for readability, code structuring and exhaustiveness. So my opinion on whether the option to assert exhaustiveness should exist or not: I really don't know. There could be some herbivore compromises like throwing IllegalStateException in the synthetic default branch, which both fits semantically and kind of solves the problem, but this is still a compromise. This is kind of a double-edged blade here and having things like that is not in Java's fashion. Although,throwing an exception in case of incorrect assertion is too high a price to pay to discard this feature completely, sacrificing enhanced readability, structuring and exhaustiveness? I think this is up to you as language designers to decide. I, as a regular user, would say that I would be willing to pay that price if I had the option to. It is also a unique feature to have, a selling point of Java in some sense. Also, maybe it is just me, but this switch over syntax seems just really neat and being really descriptive about what this switch statement/expression is responsible for. On Mon, Jun 3, 2024 at 7:20?PM Brian Goetz > wrote: If I understand correctly, member patterns are a way to say "I declare a, possibly exhaustive, set of states that class that patterns are members of could take". This is a good thing obviously, but this only works if one has access to source code of the class one wants to declare states of. To be more precise, using your terminology: - A _deconstruction pattern_ declares a state that all instances of the current class must be in. (This derives from deconstruction patterns being total.) - An _instance pattern_ declares a state that some instances of the current class can be in. Under the proposed ?case pattern? approach, a set of such patterns can indicate that they cover all instances of the class. - A _static pattern_ declares a state that some instances of _some other_ class can be in. Same story about ?case pattern?, though it is far more likely people will make incorrect assumptions / implementations about exhaustiveness when they are working at arms length. So I believe that what you want is handled by static patterns, optionally augmented with some mechanism for exhaustiveness. I suspect that you are more interested in the exhaustiveness part than the static pattern part, but I think there are diminishing returns there. Any statement of ?this set of patterns is exhaustive? is not trustable, so the compiler always has to insert a synthetic default anyway. What am I asking for / suggesting, is to also add the possibility to declare such set of states for external classes. More formally, I want to be able to state something like "I declare a set of states X that type Y could take in the subject area Z, that could be exhaustive if asserted". Using an example with tokes, I could reformulate this as follows: "I declare a set of states TokenKinds that type String could take in the subject area "Tokens", that is exhaustive by assertion". Subject area is not something present in language semantics, more of a topic that unites this set of states. Applying to CompletableFuture, this feature could introduce something like: switch (future) over AsyncStates { case Completed(var result) -> ... case Interrupted -> ... case Failed(var ex) -> ... } Note that "over AsyncStates" asserts that only patterns from set of states AsyncStates could be present This could help adapt existing APIs to new java paradigms and features without having to modify them directly, which is in many cases at least unpleasant. Also, this opens a world of possibilities for users, by enabling them to use custom patterns on types they cant access source code of. Not limited to the Java standard library, It could be also useful in frameworks, with tasks like checking if principal is anonymous/logged in/unauthorized, and a huge pile of other ways that people could apply this feature. On Mon, Apr 29, 2024 at 6:27?PM Brian Goetz > wrote: On 4/29/2024 10:02 AM, Olexandr Rotan wrote: > I think I did a really poor job expressing my thoughts in the first > message. I will try to be more clear now, along with some situations I > have encountered myself. > > Assume we have a stateless math expressions parser. For simplicity, > let's assume that we split expressions into several types: > "computation-heavy", "computation-lightweight", "erroneous" (permits > null as input) and "computation-remote" (delegate parsing to another > service via some communication protocol), and types can be assigned > heuristically (speculatively). For some tasks, we need to perform some > pre- and postprocessing around core parsing logic, like result > caching, wrapping parsing into virtual thread and registering it in > phaser etc., for others - log warning or error, or fail parsing with > exception. If you are envisioning side-effects, then I think you are already abusing patterns. Patterns either match, or they don't, and if they do, they may produce bindings to describe witnesses to the match. Exceptions are not available to you as a normal means of "something went wrong"; in writing patterns (you should think of throwing an exception from a pattern declaration as being only a few percent less drastic than calling System.exit()). Patterns, as explained in the various writeups, are the dual (deconstruction) of certain methods (aggregations.) I don't see the duality here. (If I had to guess, you're trying to bootstrap your way to parser combinators with patterns, but that's a different feature.) So I'm still not sure that you're using patterns right, so I still want to focus on that before we start inventing new features. Case patterns are a form of ad-hoc exhaustiveness, to be used only when other sources of exhaustiveness (e.g., enums, sealed types, ADTs) fail. It isn't clear to me yet that these other sources have failed. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Jun 4 17:51:25 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 4 Jun 2024 13:51:25 -0400 Subject: Best Practices for New Language Feature Usage In-Reply-To: References: Message-ID: <3d2c08b7-c186-469e-a195-8181c43d2af3@oracle.com> I would like for such discussions to be in scope, but I am also mindful that it is easy for such a discussion to generate a 300-message-long thread that is 99% opinion-based (in which case it would overwhelm the primary traffic on the list.) Regardless, I would recommend against calling *anything* "Best Practices"; this is a term that was mostly marketing when it was first invented, and has deprived of whatever meaning it had left since.? (Originally it was coined by a consulting company in the 80s, but at least had the benefit of having derived said practices from observing what their clients actually did in practice, and how it worked out.? Most claims of "Best Practice" made by developers today amount to "I like this better and want to justify it.") So I recommend a slightly less lofty-goal, which is discussing whether specific patterns should or should not be encouraged by tooling, and let these percolate into style guides as appropriate (but let that happen somewhere else.) On 5/27/2024 5:03 PM, David Alayachew wrote: > > I maintain a static analysis tool for Java, called Checkstyle > . We do our best to stay up > to date with support for parsing the latest Java syntax, sometimes it > is hard to know what best practices are for a given feature, > especially when it is still in preview (we try to support high-demand > preview features if they have 2-3 preview releases). Would this > mailing list be an appropriate place to inquire about and discuss how > to correctly (and incorrectly) use new language features? > > For example, I was thinking of creating a post called "Java 21 Record > Pattern Best Practices". My hope would be that perhaps some of the > folks that worked on delivering, testing, or just using this feature > could comment on good usages, things they have seen that are a bad > idea, and so on. This would help Checkstyle immensely by inspiring the > creation of new ways to analyze code and help developers to use these > new features in the best possible way. -------------- next part -------------- An HTML attachment was scrubbed... URL: From ella.ananeva at oracle.com Wed Jun 5 18:45:57 2024 From: ella.ananeva at oracle.com (Ella Ananeva) Date: Wed, 5 Jun 2024 18:45:57 +0000 Subject: This expression in lambda in early construction context Message-ID: Hi, I understand that JEP 482 allows to refer to this in the early construction context if this expression is in the left part of an assignment expression: class Test { int a; Test() { this.a = 1; super(); } } This compiles just fine (I have JDK 23 build 25). However, when I try to do the same in a lambda body, I get a compilation error: interface Foo { void foo(); } class Test { int a; Test() { Foo lmb = () -> { this.a = 1;}; super(); } } Test.java:9:27 java: cannot reference this before supertype constructor has been called Is lambda body some special case? Thank you, Ella Ananeva -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Jun 5 19:33:37 2024 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 5 Jun 2024 21:33:37 +0200 (CEST) Subject: This expression in lambda in early construction context In-Reply-To: References: Message-ID: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> > From: "Ella Ananeva" > To: "amber-dev" > Sent: Wednesday, June 5, 2024 8:45:57 PM > Subject: This expression in lambda in early construction context > Hi, > I understand that JEP 482 allows to refer to this in the early construction > context if this expression is in the left part of an assignment expression: > class Test { > int a; > Test() { > this.a = 1; > super(); > } > } > This compiles just fine (I have JDK 23 build 25). > However, when I try to do the same in a lambda body, I get a compilation error: > interface Foo { > void foo (); > } > class Test { > int a ; > Test () { > Foo lmb = () -> { this .a = 1 ;}; > super (); > } > } > Test.java:9:27 > java: cannot reference this before supertype constructor has been called > Is lambda body some special case? The lambda capture "this" (it is a parameter of the call to the construction of the lambda), but "this" as a value is only available after the call to the super constructor. > Thank you, > Ella Ananeva regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From ella.ananeva at oracle.com Wed Jun 5 20:30:54 2024 From: ella.ananeva at oracle.com (Ella Ananeva) Date: Wed, 5 Jun 2024 20:30:54 +0000 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hi Remi, Good point! I get it that the value of this is not accessible in lambda body before the superconstructor call. However, we cannot access the value of this in the early construction context even without lambda: class Test { int a; Test() { this.a = 1; int a = this.a; // compilation error super(); } } JEP 482 specifically says that in the early construction context the value of this is not accessible, that we only can assigned the value to the fields of this class. Why should the rules for a lambda be different? I?d say, if we want a special behavior for lambda, it should be described in the specification, shouldn?t it? Thanks, Ella From: Remi Forax Date: Wednesday, June 5, 2024 at 12:33?PM To: Ella Ananeva Cc: amber-dev Subject: [External] : Re: This expression in lambda in early construction context ________________________________ From: "Ella Ananeva" To: "amber-dev" Sent: Wednesday, June 5, 2024 8:45:57 PM Subject: This expression in lambda in early construction context Hi, I understand that JEP 482 allows to refer to this in the early construction context if this expression is in the left part of an assignment expression: class Test { int a; Test() { this.a = 1; super(); } } This compiles just fine (I have JDK 23 build 25). However, when I try to do the same in a lambda body, I get a compilation error: interface Foo { void foo(); } class Test { int a; Test() { Foo lmb = () -> { this.a = 1;}; super(); } } Test.java:9:27 java: cannot reference this before supertype constructor has been called Is lambda body some special case? The lambda capture "this" (it is a parameter of the call to the construction of the lambda), but "this" as a value is only available after the call to the super constructor. Thank you, Ella Ananeva regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Wed Jun 5 20:43:27 2024 From: chen.l.liang at oracle.com (Chen Liang) Date: Wed, 5 Jun 2024 20:43:27 +0000 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hi Ella, What do you mean by "Why should the rules for a lambda be different"? Lambda bodies are not part of the early-construction context, as the lambda can be invoked anywhere and anytime after it's created, including before the super constructor call completes (such as when it's passed as a parameter to super constructor). Best, Chen ________________________________ From: amber-dev on behalf of Ella Ananeva Sent: Wednesday, June 5, 2024 3:30 PM To: Remi Forax ; amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Hi Remi, Good point! I get it that the value of this is not accessible in lambda body before the superconstructor call. However, we cannot access the value of this in the early construction context even without lambda: class Test { int a; Test() { this.a = 1; int a = this.a; // compilation error super(); } } JEP 482 specifically says that in the early construction context the value of this is not accessible, that we only can assigned the value to the fields of this class. Why should the rules for a lambda be different? I?d say, if we want a special behavior for lambda, it should be described in the specification, shouldn?t it? Thanks, Ella From: Remi Forax Date: Wednesday, June 5, 2024 at 12:33?PM To: Ella Ananeva Cc: amber-dev Subject: [External] : Re: This expression in lambda in early construction context ________________________________ From: "Ella Ananeva" To: "amber-dev" Sent: Wednesday, June 5, 2024 8:45:57 PM Subject: This expression in lambda in early construction context Hi, I understand that JEP 482 allows to refer to this in the early construction context if this expression is in the left part of an assignment expression: class Test { int a; Test() { this.a = 1; super(); } } This compiles just fine (I have JDK 23 build 25). However, when I try to do the same in a lambda body, I get a compilation error: interface Foo { void foo(); } class Test { int a; Test() { Foo lmb = () -> { this.a = 1;}; super(); } } Test.java:9:27 java: cannot reference this before supertype constructor has been called Is lambda body some special case? The lambda capture "this" (it is a parameter of the call to the construction of the lambda), but "this" as a value is only available after the call to the super constructor. Thank you, Ella Ananeva regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From ella.ananeva at oracle.com Wed Jun 5 21:29:07 2024 From: ella.ananeva at oracle.com (Ella Ananeva) Date: Wed, 5 Jun 2024 21:29:07 +0000 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Thanks for pointing this out, Chen. Please bear with me for a moment. If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right? class Main { int a; Main() { this.a = 1; //line A Foo lmb = () -> this.a = 1; lmb.foo(); //line B super(); } The statement in line A assigns a value to a member field before the superconstructor call. Lambda also assigns the value to a field and is invoked before the superconstructor call. I?d say the difference in this behavior makes sense only if the compiler doesn?t attempt to initialize the field in line A but tries to initialize it when the lambda is invoked. It?s like line A is just an instruction for later, and in line B a real initialization attempt takes place. Is this what is happening? From: Chen Liang Date: Wednesday, June 5, 2024 at 1:43?PM To: Ella Ananeva , amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Hi Ella, What do you mean by "Why should the rules for a lambda be different"? Lambda bodies are not part of the early-construction context, as the lambda can be invoked anywhere and anytime after it's created, including before the super constructor call completes (such as when it's passed as a parameter to super constructor). Best, Chen ________________________________ From: amber-dev on behalf of Ella Ananeva Sent: Wednesday, June 5, 2024 3:30 PM To: Remi Forax ; amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Hi Remi, Good point! I get it that the value of this is not accessible in lambda body before the superconstructor call. However, we cannot access the value of this in the early construction context even without lambda: class Test { int a; Test() { this.a = 1; int a = this.a; // compilation error super(); } } JEP 482 specifically says that in the early construction context the value of this is not accessible, that we only can assigned the value to the fields of this class. Why should the rules for a lambda be different? I?d say, if we want a special behavior for lambda, it should be described in the specification, shouldn?t it? Thanks, Ella From: Remi Forax Date: Wednesday, June 5, 2024 at 12:33?PM To: Ella Ananeva Cc: amber-dev Subject: [External] : Re: This expression in lambda in early construction context ________________________________ From: "Ella Ananeva" To: "amber-dev" Sent: Wednesday, June 5, 2024 8:45:57 PM Subject: This expression in lambda in early construction context Hi, I understand that JEP 482 allows to refer to this in the early construction context if this expression is in the left part of an assignment expression: class Test { int a; Test() { this.a = 1; super(); } } This compiles just fine (I have JDK 23 build 25). However, when I try to do the same in a lambda body, I get a compilation error: interface Foo { void foo(); } class Test { int a; Test() { Foo lmb = () -> { this.a = 1;}; super(); } } Test.java:9:27 java: cannot reference this before supertype constructor has been called Is lambda body some special case? The lambda capture "this" (it is a parameter of the call to the construction of the lambda), but "this" as a value is only available after the call to the super constructor. Thank you, Ella Ananeva regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Wed Jun 5 21:59:39 2024 From: chen.l.liang at oracle.com (Chen Liang) Date: Wed, 5 Jun 2024 21:59:39 +0000 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: > If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right? It actually can. In fact, lambdas are just like any objects: wherever the object is accessible, its public methods are accessible, so the lambda can be invoked outside of the constructor if it's passed somewhere else, such as being stored in an instance (non-static) field. As a result, for now, if you know you executed another method that takes a lambda (like ClassFile.build()) and that lambda is executed, the best bet for you is to declare an array like: int[] value = new int[0]; // uses a mutable object (here a length-1 array) to capture value api.execute(arguments, lambdaArg -> { value[0] = lambdaArg; }); this.earlyField = value[0]; // maybe add checks to ensure value is properly initialized If we just allow assignment to early field in a lambda, it's possible for api to send this lambda into an executor that calls the lambda with 5 second delay; that scenario is clearly not "early-construction context" and the language rule aims to prevent such misuse. - Chen ________________________________ From: Ella Ananeva Sent: Wednesday, June 5, 2024 4:29 PM To: Chen Liang ; amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Thanks for pointing this out, Chen. Please bear with me for a moment. If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right? class Main { int a; Main() { this.a = 1; //line A Foo lmb = () -> this.a = 1; lmb.foo(); //line B super(); } The statement in line A assigns a value to a member field before the superconstructor call. Lambda also assigns the value to a field and is invoked before the superconstructor call. I?d say the difference in this behavior makes sense only if the compiler doesn?t attempt to initialize the field in line A but tries to initialize it when the lambda is invoked. It?s like line A is just an instruction for later, and in line B a real initialization attempt takes place. Is this what is happening? From: Chen Liang Date: Wednesday, June 5, 2024 at 1:43?PM To: Ella Ananeva , amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Hi Ella, What do you mean by "Why should the rules for a lambda be different"? Lambda bodies are not part of the early-construction context, as the lambda can be invoked anywhere and anytime after it's created, including before the super constructor call completes (such as when it's passed as a parameter to super constructor). Best, Chen ________________________________ From: amber-dev on behalf of Ella Ananeva Sent: Wednesday, June 5, 2024 3:30 PM To: Remi Forax ; amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Hi Remi, Good point! I get it that the value of this is not accessible in lambda body before the superconstructor call. However, we cannot access the value of this in the early construction context even without lambda: class Test { int a; Test() { this.a = 1; int a = this.a; // compilation error super(); } } JEP 482 specifically says that in the early construction context the value of this is not accessible, that we only can assigned the value to the fields of this class. Why should the rules for a lambda be different? I?d say, if we want a special behavior for lambda, it should be described in the specification, shouldn?t it? Thanks, Ella From: Remi Forax Date: Wednesday, June 5, 2024 at 12:33?PM To: Ella Ananeva Cc: amber-dev Subject: [External] : Re: This expression in lambda in early construction context ________________________________ From: "Ella Ananeva" To: "amber-dev" Sent: Wednesday, June 5, 2024 8:45:57 PM Subject: This expression in lambda in early construction context Hi, I understand that JEP 482 allows to refer to this in the early construction context if this expression is in the left part of an assignment expression: class Test { int a; Test() { this.a = 1; super(); } } This compiles just fine (I have JDK 23 build 25). However, when I try to do the same in a lambda body, I get a compilation error: interface Foo { void foo(); } class Test { int a; Test() { Foo lmb = () -> { this.a = 1;}; super(); } } Test.java:9:27 java: cannot reference this before supertype constructor has been called Is lambda body some special case? The lambda capture "this" (it is a parameter of the call to the construction of the lambda), but "this" as a value is only available after the call to the super constructor. Thank you, Ella Ananeva regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From ella.ananeva at oracle.com Wed Jun 5 22:04:23 2024 From: ella.ananeva at oracle.com (Ella Ananeva) Date: Wed, 5 Jun 2024 22:04:23 +0000 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Thank you so much! It makes sense now. From: Chen Liang Date: Wednesday, June 5, 2024 at 2:59?PM To: Ella Ananeva , amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context > If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right? It actually can. In fact, lambdas are just like any objects: wherever the object is accessible, its public methods are accessible, so the lambda can be invoked outside of the constructor if it's passed somewhere else, such as being stored in an instance (non-static) field. As a result, for now, if you know you executed another method that takes a lambda (like ClassFile.build()) and that lambda is executed, the best bet for you is to declare an array like: int[] value = new int[0]; // uses a mutable object (here a length-1 array) to capture value api.execute(arguments, lambdaArg -> { value[0] = lambdaArg; }); this.earlyField = value[0]; // maybe add checks to ensure value is properly initialized If we just allow assignment to early field in a lambda, it's possible for api to send this lambda into an executor that calls the lambda with 5 second delay; that scenario is clearly not "early-construction context" and the language rule aims to prevent such misuse. - Chen ________________________________ From: Ella Ananeva Sent: Wednesday, June 5, 2024 4:29 PM To: Chen Liang ; amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Thanks for pointing this out, Chen. Please bear with me for a moment. If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right? class Main { int a; Main() { this.a = 1; //line A Foo lmb = () -> this.a = 1; lmb.foo(); //line B super(); } The statement in line A assigns a value to a member field before the superconstructor call. Lambda also assigns the value to a field and is invoked before the superconstructor call. I?d say the difference in this behavior makes sense only if the compiler doesn?t attempt to initialize the field in line A but tries to initialize it when the lambda is invoked. It?s like line A is just an instruction for later, and in line B a real initialization attempt takes place. Is this what is happening? From: Chen Liang Date: Wednesday, June 5, 2024 at 1:43?PM To: Ella Ananeva , amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Hi Ella, What do you mean by "Why should the rules for a lambda be different"? Lambda bodies are not part of the early-construction context, as the lambda can be invoked anywhere and anytime after it's created, including before the super constructor call completes (such as when it's passed as a parameter to super constructor). Best, Chen ________________________________ From: amber-dev on behalf of Ella Ananeva Sent: Wednesday, June 5, 2024 3:30 PM To: Remi Forax ; amber-dev Subject: Re: [External] : Re: This expression in lambda in early construction context Hi Remi, Good point! I get it that the value of this is not accessible in lambda body before the superconstructor call. However, we cannot access the value of this in the early construction context even without lambda: class Test { int a; Test() { this.a = 1; int a = this.a; // compilation error super(); } } JEP 482 specifically says that in the early construction context the value of this is not accessible, that we only can assigned the value to the fields of this class. Why should the rules for a lambda be different? I?d say, if we want a special behavior for lambda, it should be described in the specification, shouldn?t it? Thanks, Ella From: Remi Forax Date: Wednesday, June 5, 2024 at 12:33?PM To: Ella Ananeva Cc: amber-dev Subject: [External] : Re: This expression in lambda in early construction context ________________________________ From: "Ella Ananeva" To: "amber-dev" Sent: Wednesday, June 5, 2024 8:45:57 PM Subject: This expression in lambda in early construction context Hi, I understand that JEP 482 allows to refer to this in the early construction context if this expression is in the left part of an assignment expression: class Test { int a; Test() { this.a = 1; super(); } } This compiles just fine (I have JDK 23 build 25). However, when I try to do the same in a lambda body, I get a compilation error: interface Foo { void foo(); } class Test { int a; Test() { Foo lmb = () -> { this.a = 1;}; super(); } } Test.java:9:27 java: cannot reference this before supertype constructor has been called Is lambda body some special case? The lambda capture "this" (it is a parameter of the call to the construction of the lambda), but "this" as a value is only available after the call to the super constructor. Thank you, Ella Ananeva regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jun 7 03:00:52 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 6 Jun 2024 23:00:52 -0400 Subject: How to help with Primitive Patterns Message-ID: Hello Amber Dev Team, Project Amber made it very clear that the most effective way to help out is by trying out Preview features in a(n ideally, nontrivial) project, and then posting feedback to the mailing lists. I've made it a point to do that for every Amber feature (minus the ones still in Preview that I haven't caught up to yet). However, Primitive Patterns is probably the first time where I genuinely struggle to come up with a project that can use these in a non-trivial way. I am offering up my time and services here -- can someone help me with coming up a project idea to test out the feature? Just an idea is all I need, I'll shepherd it all the way to a finished project. Just need someone to "prime the pump," so to speak. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 7 21:34:43 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 7 Jun 2024 22:34:43 +0100 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hi Ella, the problem here is that, since the lambda needs to refer to "this", it's as if you need to pass "this" to create the lambda object. To make it simpler, you can think of the lambda as a local class (not entirely accurate, but I think it paints a good analogy for what's going on here): class Main { ??? int a; ??? Main() { ??????? this.a = 1; //line A ??????? class Foo { ????????????? Main this$0; ????????????? Foo(Main this$0) { this.this$0 = this$0; } ????????????? void run() { this$0.a = 1; } ??????? } ??????? Foo lmb = new Foo(this); // line A ??????? lmb.run(); //line B ??????? super(); ??? } Like before, in line (A) we create the lambda expression (here modelled as a local class). Note that the local class wants a construction parameter, of type Main (since the local class needs to refer to it). But then, in line (B), we need to supply an argument of type Main, namely "this". So here we are effectively reading/accessing "this" before the super constructor has been called. The lambda expression, being so compact, hides a bit of the problem and you are right that it's a bit confusing at first, but I hope the example above clarifies things a bit. Cheers Maurizio On 05/06/2024 22:29, Ella Ananeva wrote: > > Thanks for pointing this out, Chen. > > Please bear with me for a moment. If we have a lambda as a local > variable in the constructor, it cannot be invoked outside of the > constructor, right? > > class Main { > int a; > Main() { > this.a = 1; ///line A > /Foo lmb = () -> this.a = 1;// > > //lmb.foo(); //line B > super(); > ??? } > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jun 7 22:10:22 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 7 Jun 2024 17:10:22 -0500 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: I think Ella may have a valid point from the point of view of the specification - i.e., where in the new spec does it specify that the new LHS field assignment exception doesn't apply within a lambda? ?6.5.6.1 says: If the expression name appears in an early construction context of C > (8.8.7.1), then it is the left-hand operand of a simple assignment > expression (15.26), and the declaration of the named variable lacks an > initializer. But the definition of early construction context includes all contained lambdas, subclasses, etc. So maybe it should instead say something like: If the expression name appears in an early construction context of C > (8.8.7.1), then it is the left-hand operand of a simple assignment > expression (15.26), the declaration of the named variable lacks an > initializer, and the expression is not contained in any lambda or class > declaration that is contained in C. -Archie On Fri, Jun 7, 2024 at 4:35?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > Hi Ella, > the problem here is that, since the lambda needs to refer to "this", it's > as if you need to pass "this" to create the lambda object. > > To make it simpler, you can think of the lambda as a local class (not > entirely accurate, but I think it paints a good analogy for what's going on > here): > > class Main { > int a; > Main() { > this.a = 1; //line A > class Foo { > Main this$0; > Foo(Main this$0) { this.this$0 = this$0; } > void run() { this$0.a = 1; } > } > Foo lmb = new Foo(this); // line A > > lmb.run(); //line B > super(); > } > > > Like before, in line (A) we create the lambda expression (here modelled as > a local class). Note that the local class wants a construction parameter, > of type Main (since the local class needs to refer to it). But then, in > line (B), we need to supply an argument of type Main, namely "this". So > here we are effectively reading/accessing "this" before the super > constructor has been called. > > The lambda expression, being so compact, hides a bit of the problem and > you are right that it's a bit confusing at first, but I hope the example > above clarifies things a bit. > > Cheers > Maurizio > On 05/06/2024 22:29, Ella Ananeva wrote: > > Thanks for pointing this out, Chen. > > Please bear with me for a moment. If we have a lambda as a local variable > in the constructor, it cannot be invoked outside of the constructor, right? > > > > class Main { > int a; > Main() { > this.a = 1; // > *line A *Foo lmb = () -> this.a = 1; > > lmb.foo(); //line B > super(); > } > > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From sam at slfc.dev Sun Jun 9 00:20:39 2024 From: sam at slfc.dev (Sam Carlberg) Date: Sun, 09 Jun 2024 00:20:39 +0000 Subject: Reducing verbosity for generics Message-ID: I developed and maintain a units of measurement library. The target audience is educational - predominantly high school students with minimal experience with Java or programming in general - so its API must be accessible, readable, and type safe for anyone to use it. Otherwise they would just use a raw double? and try to keep track of unit conversions themselves (usually in code comments, and often incorrectly - thus the library). The core part of the library is a Unit? class with a recursive self type to make unit interactions and conversions type safe, and a generic Measure? record that holds values in terms of an arbitrary unit. Unit? is subclassed for concrete unit types (think distance, time, temperature, and so on) and also mathematical types representing quotient and product types (think velocity, area, or force). The self type is necessary to allow methods in the unit class to have a factory method for returning measurements in terms of itself, and for providing dimensional analysis in the measure record, but causes myriad problems with type inheritance more than one level deep. This class setup works very well for modeling units and their interactions. A velocity can be represented as Quotient?; force can be represented (rather verbosely) as Product, Time>>?. The problem is that the type compositions only represent the underlying building blocks; they completely fail to express what the results of the compositions actually mean. Imagine a new programmer given the choice between representing torque as Product, Time>>, Distance>? with the type-safe unit API or with a raw double? - they'd (rightly) dismiss the unit outright as being too complex and just use a raw double?, trying their best to handle unit safety on their own. Even though the library can express unit types correctly and exhaustively, it cannot do so ergonomically, and so the users who would most benefit from unit safety would never use it. However, nobody would run screaming from a type simply named Torque?. But here we run into limitations in the type system: var? only applies to local variables, not fields, and explicit types may sometimes still be desired for pedagogy; simple subclassing fails due to the recursive self type not being inherited more than one level down; "bubbling up" the self type for subclasses to specify pollutes any usages of the intermediate types; wrappers have a different type signature, making them incompatible with the types they wrap; and flattening the hierarchy to avoid subclassing results in frustrating edge cases like Velocity? and Quotient? being incompatible types even though they both represent the same thing. A way to define type aliases is the most obvious solution (but perhaps not the best): - We can define direct, expressive type names for units without needing to create specialized subclasses - Because there's no inheritance, the recursive self-type problem disappears - Aliases would mean Velocity? and Quotient? are exactly the same type, and would therefore always be two-way compatible I'm not proposing any particular syntax, or even to add C#-style using? or Kotlin's typealias. Just that a way of declaring in code that velocity is shorthand for Quotient?, or that Torque? is shorthand for that monstrous set of nested generics, would neatly resolve all the tradeoffs between verbosity and type safety. I figured amber is a good place to bring this up, given the project's focus on improving developer ergonomics and onboarding of new programmers. ~ Sam ps: I have made significant simplifications to my examples for the sake of brevity. I'd be happy to clarify anything or share code samples if it would be useful -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Sun Jun 9 22:20:13 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Sun, 9 Jun 2024 17:20:13 -0500 Subject: Reducing verbosity for generics In-Reply-To: References: Message-ID: On Sat, Jun 8, 2024 at 7:21?PM Sam Carlberg wrote: > The core part of the library is a Unit class with a recursive self type Side note/digression on "self type"... I've also wished for a simpler way to create self types that doesn't spam your entire type hierarchy with a new generic type variable. For example, instead of this: public interface Builder> { B withFoo(Foo f); B withBar(Bar b); T build(); } syntactic sugar could allow you to do something like this: public interface Builder { this.class withFoo(Foo f); this.class withBar(Bar b); T build(); } The compiler would then assign the return value of any withFoo() invocation to have the exact same (fully generified) type as the type of the receiver, and any implementation of withFoo() would just have to return an object with type compatible with Builder. I don't see any fundamental reason why this couldn't work but I could be missing something. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From johannes.spangenberg at hotmail.de Mon Jun 10 21:05:03 2024 From: johannes.spangenberg at hotmail.de (Johannes Spangenberg) Date: Mon, 10 Jun 2024 23:05:03 +0200 Subject: Reducing verbosity for generics In-Reply-To: References: Message-ID: > and any implementation of withFoo() would just have to return an > object with type compatible with Builder. With these rules, the compiler would allow the following code, although it is broken: public class Builder { // May have sub-types ... this.class copy() { return new Builder(this); } ... } Anyway, there are probably solutions to make it work in most situations using more elaborate rules for the type system. I guess you could also argue that it is OK that the compiler cannot detect the broken code above, and the application should just throw a ClassCastException. After all, it wouldn't be the only case where broken code is not detect at compile time. However, I think such change would require careful consideration of different solutions to the problem (and whether the problem justifies special syntax and rules). -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Mon Jun 10 22:00:30 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Tue, 11 Jun 2024 01:00:30 +0300 Subject: Reducing verbosity for generics In-Reply-To: References: Message-ID: If I were to propose the solution, I would make implementation of methods that return this.class uninheritable if its parent impl not returns this.class type, i.e. each subtype obliged to redefine method unless it's parent delegates return to a method that returns this.class itself. Effectively this would mean that each type must redefine end of each chain of methods that return this.class. This would reduce inheritence effort to effectively having to override 1 (in good design) supplier method and still allow to inherit actual logic from super classes. Although, I must say that implementing such feature would be a headache for sure. You must account all corner cases if adopting approach I described, because, at least to me, it seems a bit shaky. Also, I am not sure that complexity of this approach really justified by solved problem. I feel like, whatever you can come up with, it is either incredibly complex or not very effective at solving described problems On Tue, Jun 11, 2024, 00:05 Johannes Spangenberg < johannes.spangenberg at hotmail.de> wrote: > and any implementation of withFoo() would just have to return an object > with type compatible with Builder. > > With these rules, the compiler would allow the following code, although it > is broken: > > public class Builder { // May have sub-types > ... > this.class copy() { > return new Builder(this); > } > ... > } > > Anyway, there are probably solutions to make it work in most situations > using more elaborate rules for the type system. I guess you could also > argue that it is OK that the compiler cannot detect the broken code above, > and the application should just throw a ClassCastException. After all, it > wouldn't be the only case where broken code is not detect at compile time. > However, I think such change would require careful consideration of > different solutions to the problem (and whether the problem justifies > special syntax and rules). > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Mon Jun 10 22:53:40 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 10 Jun 2024 17:53:40 -0500 Subject: Reducing verbosity for generics In-Reply-To: References: Message-ID: On Mon, Jun 10, 2024 at 4:05?PM Johannes Spangenberg < johannes.spangenberg at hotmail.de> wrote: > and any implementation of withFoo() would just have to return an object > with type compatible with Builder. > > With these rules, the compiler would allow the following code, although it > is broken: > > public class Builder { // May have sub-types > ... > this.class copy() { > return new Builder(this); > } > ... > } > > Oops, you're right I didn't think of that. But there's an easy fix :) public class Builder { private final Function copyConstructor; protected Builder(Function copyConstructor) { this.copyConstructor = copyConstructor; } public this.class copy() { return this.copyConstructor.apply(this); } } > Anyway, there are probably solutions to make it work in most situations > using more elaborate rules for the type system. I guess you could also > argue that it is OK that the compiler cannot detect the broken code above, > and the application should just throw a ClassCastException. After all, it > wouldn't be the only case where broken code is not detect at compile time. > However, I think such change would require careful consideration of > different solutions to the problem (and whether the problem justifies > special syntax and rules). > Agree.. there could be proper checks but it starts to get a little ugly. The compiler would disallow classes that override a supertype containing such methods without overriding each of them. For completeness you'd also want to detect violations at the JVM level as well and throw IncompatibleClassChangeError, etc. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Mon Jun 10 23:00:52 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 10 Jun 2024 18:00:52 -0500 Subject: Reducing verbosity for generics In-Reply-To: References: Message-ID: On Mon, Jun 10, 2024 at 5:01?PM Olexandr Rotan wrote: > Also, I am not sure that complexity of this approach really justified by > solved problem. > I agree, it's just a thought. The self-type hierarchy spam problem also creates a high level of pain/complexity though. I feel bad now for hijacking Sam's original email so I'll end my digression... -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From sam at slfc.dev Mon Jun 10 23:53:11 2024 From: sam at slfc.dev (Sam Carlberg) Date: Mon, 10 Jun 2024 23:53:11 +0000 Subject: Reducing verbosity for generics In-Reply-To: References: Message-ID: An improved method for expressing the self-type would certainly help, I think. Though I can see it failing with inheritance in generics: ``` record Box(T value) {} class A { Box boxed() { return new Box<>(this); } } class B extends A {} B b = new B(); A upcast = b; Box boxA = upcast.boxed(); Box boxB = b.boxed(); ``` Box? and Box? are incompatible types. So even though boxA? and boxB? are equivalent objects, we cannot assign boxA = boxB? or have a method that accepts Box? also accept Box?. Wildcards could help, except if your target demographic has little-to-no experience with generics. Anyway. There's still the core issue that I'm trying to solve, which is giving meaningful names to complex generic types. Subclassing helps (and a "this" type would make it much better), but it still runs into issues where part of a library would want to use the base type and user code wants to use a subclass with a name meaningful to them (eg users wanting Velocity? and library code wanting generic Quotient? which may or may not fit the Velocity? type contract). I'm having a hard time seeing any solution other than allowing types to be aliased - there'd be no potential for incompatible inherited types there's no inheritance at all. - Sam On Monday, June 10th, 2024 at 7:00 PM, Archie Cobbs wrote: > On Mon, Jun 10, 2024 at 5:01?PM Olexandr Rotan wrote: > >> Also, I am not sure that complexity of this approach really justified by solved problem. > > I agree, it's just a thought. The self-type hierarchy spam problem also creates a high level of pain/complexity though. > > I feel bad now for hijacking Sam's original email so I'll end my digression... > > -Archie > -- > > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Wed Jun 12 09:05:55 2024 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Wed, 12 Jun 2024 09:05:55 +0000 Subject: [External] : Re: This expression in lambda in early construction context In-Reply-To: References: <1144618915.42536336.1717616017954.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Thanks Ella. Does look like we need to strengthen the spec. Archie?s change clearly works for this example, but I am a little worried that it?s fragile. I will need to check it. Not sure we are going to make the 23 train with this though?will let you know when I have something. Thanks, Gavin On 7 Jun 2024, at 23:10, Archie Cobbs wrote: I think Ella may have a valid point from the point of view of the specification - i.e., where in the new spec does it specify that the new LHS field assignment exception doesn't apply within a lambda? ?6.5.6.1 says: If the expression name appears in an early construction context of C (8.8.7.1), then it is the left-hand operand of a simple assignment expression (15.26), and the declaration of the named variable lacks an initializer. But the definition of early construction context includes all contained lambdas, subclasses, etc. So maybe it should instead say something like: If the expression name appears in an early construction context of C (8.8.7.1), then it is the left-hand operand of a simple assignment expression (15.26), the declaration of the named variable lacks an initializer, and the expression is not contained in any lambda or class declaration that is contained in C. -Archie On Fri, Jun 7, 2024 at 4:35?PM Maurizio Cimadamore > wrote: Hi Ella, the problem here is that, since the lambda needs to refer to "this", it's as if you need to pass "this" to create the lambda object. To make it simpler, you can think of the lambda as a local class (not entirely accurate, but I think it paints a good analogy for what's going on here): class Main { int a; Main() { this.a = 1; //line A class Foo { Main this$0; Foo(Main this$0) { this.this$0 = this$0; } void run() { this$0.a = 1; } } Foo lmb = new Foo(this); // line A lmb.run(); //line B super(); } Like before, in line (A) we create the lambda expression (here modelled as a local class). Note that the local class wants a construction parameter, of type Main (since the local class needs to refer to it). But then, in line (B), we need to supply an argument of type Main, namely "this". So here we are effectively reading/accessing "this" before the super constructor has been called. The lambda expression, being so compact, hides a bit of the problem and you are right that it's a bit confusing at first, but I hope the example above clarifies things a bit. Cheers Maurizio On 05/06/2024 22:29, Ella Ananeva wrote: Thanks for pointing this out, Chen. Please bear with me for a moment. If we have a lambda as a local variable in the constructor, it cannot be invoked outside of the constructor, right? class Main { int a; Main() { this.a = 1; //line A Foo lmb = () -> this.a = 1; lmb.foo(); //line B super(); } -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Wed Jun 12 12:07:01 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Wed, 12 Jun 2024 13:07:01 +0100 Subject: known translation issues related to JEP 482 Message-ID: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> Hi, as we discussed through the details of the new JLS rules in JEP 482, we have found several issues where javac is emitting incorrect code (e.g. attempting to capture enclosing instances that are still under construction). In order to allow us to look at these issues more holistically, I've created a new JBS label, namely "javac-pre-capture". Here's a query to show them all: https://bugs.openjdk.org/issues/?jql=labels%20%3D%20javac-pre-capture As stated above, this label is meant for issue related to issues that have to do broken translation strategy of enclosing instance references. Such issues can manifest them in different ways: * compiler crashes when compiling correct code (e.g. https://bugs.openjdk.org/browse/JDK-8334037) * "late" compiler error? (e.g. in Lower) as the compiler can't resolve an enclosing instance (e.g. https://bugs.openjdk.org/browse/JDK-8334121) * compiler succeds when compiling a correct program, but the compiled program doesn't verify (we don't have an instance of this, yet, but it's something bound to occur) The scope of this label is somewhat (deliberately) narrow. As such, the label does NOT cover other issues pertaining to JEP 482, such as * compiler attempting to translate a bad program (https://bugs.openjdk.org/browse/JDK-8334043) * compiler generating invalid code for reasons that have nothing to do with capture (https://bugs.openjdk.org/browse/JDK-8332106) (if we think that a more general label would be useful to mark all issues that have to do with pre-construction context, we can do that too). Cheers Maurizio From hjohn at xs4all.nl Wed Jun 12 14:43:48 2024 From: hjohn at xs4all.nl (John Hendrikx) Date: Wed, 12 Jun 2024 14:43:48 +0000 Subject: Srtring Templates experiment for JDBC Message-ID: Hi Amber list, I know the discussion on String Templates has come a bit to a halt. I just wanted to add my perspective after developing a library which uses the JDK 22 String Templates. I found the processor syntax, although a bit unintuitive at first, quite a nice fit for handling SQL queries with a thin wrapper around JDBC. The processor and current transactional scope can be nicely wrapped into a single reference, so you hardly notice there is a processor involved. Where previously I may have written: try (Transaction tx = db.beginTransaction()) { List rows = tx.perform("SELECT * FROM employees WHERE a = ?", a).toList(); } I now write: try (Transaction tx = db.beginTransaction()) { List rows = tx."SELECT * FROM employees WHERE a = \{a}".toList(); } Where the Transaction class implements the processor. I've been reading the chatter on the experts list, and am especially weary of solution that would not allow for an empty template. For example, it should be easily possible to have a template without parameters without having to change to a different syntax: int count = tx."SELECT COUNT(*) FROM employees".asInt().get(); As this may evolve to for example: int count = tx."SELECT COUNT(*) FROM employees WHERE archived = \{archived}".asInt().get(); (Or vice versa) For those interested in how I've been using templates, the library is found here: https://github.com/hjohn/TemplatedJDBC -- I bit the bullet and converted a lot of other (internal) projects to use this syntax, and I found it to work quite well. I'm looking forward to seeing more progress in this area and to test the next iteration of String Templates when it becomes available. --John -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Wed Jun 12 15:07:12 2024 From: ron.pressler at oracle.com (Ron Pressler) Date: Wed, 12 Jun 2024 15:07:12 +0000 Subject: Srtring Templates experiment for JDBC In-Reply-To: References: Message-ID: <3E095CA4-6A05-4AF3-BB3C-8F7A066FBC45@oracle.com> > On 12 Jun 2024, at 15:43, John Hendrikx wrote: > > > I've been reading the chatter on the experts list, and am especially weary of solution that would not allow for an empty template. I expect that any future solution will allow for an empty template. ? Ron From archie.cobbs at gmail.com Wed Jun 12 21:06:40 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Wed, 12 Jun 2024 16:06:40 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> Message-ID: Hi Maurizio, Thanks for looking into these issues. I agree this is definitely one of those "we need to rethink and refactor" opportunities. Please let me know if/how I can help. So far I've done some complaining but not much else :) I'm trying to wrap my head around what's needed and have tried to write this out in detail mainly for my own understanding. Please tell me if the below sounds correct to you. Observation: this issue doesn't really affect non-static member inner classes: they can never be declared in an early construction context of their directly enclosing class. So they always have an immediate outer instance, and it's always available and usable (or explicitly provided via qualified new). So all the complexity comes with local and anonymous classes, especially when they are declared inside early construction contexts with various levels of nesting. Fortunately, the JEP treats local and anonymous classes basically the same way. Some terminology (just placeholders for now, I'm mainly trying to nail down the logic): 1. Every class declaration C has zero or more lexically enclosing class declarations; let's call them E?, E?, E?... ordered from the inside out. 2. For ease of terminology, define the degenerate case E? = C 3. For each E?, there is possibly an *outer instance* O? *defined* for C (see below) 4. For ease of terminology, define the degenerate case O? = C.this. 5. When is O? *defined* for C? 1. O? is always defined 2. Let k ? 0 be the smallest value such that E? is static (enum, record, declared in a static context, etc.) 3. For all i ? k, the outer instance O? is defined for C and it has type E? 4. For all i > k there is no outer instance O? defined for C 6. If O? is defined, it may also be the case that O? is *accessible* in C (in non-static contexts): 1. O? is *accessible* in C if and only if C does not appear in an early construction context of E? >From the above, you can see that the outer classes for which a corresponding outer instance is defined includes C and is a contiguous range up until you hit the first static class/context. However, the set of outer classes for which the corresponding outer instance is both defined *and accessible* is an arbitrary subset of that contiguous range. In effect we create an "inaccessibility hole" at any class E? for which C is inside an early construction context of E?. Observation: Suppose O? of type E? is defined for C. Then for any h > k, O? is defined (accessible) for E? if and only if it is defined (accessible) for C. In other words, if a class F encloses both C and E?, then C and E? agree on whether F has a defined and/or accessible outer instance. This is simply because C and E? are lexically in the "same place" with respect to F. Define the *compiler outer instance* for C to be that O? for which k > 0, O? is both defined and accessible, and where k is minimal (if such a thing exists). OK now given the above what does the compiler need to do? Part 0: The compiler needs to be able to calculate, given any class C, whether C has a compiler outer instance (let's call it O?) and its type (let's call it E?) Part 1: When compiling some class C for which a compiler outer instance exists: 1. Calculate the type E? 2. Add a synthetic constructor parameter O? of type E? to each constructor of C 3. Add a synthetic field of type E? in which to store O? ("this$n") 4. Store O? in that field in each constructor of C (very first thing) Part 2: Compiling the expression "Foo.this" in the context of some class C: 1. If C has no compiler outer instance, then error unless Foo = C. 2. Otherwise let O? with type E? be the compiler outer instance for C 3. For expressions like "E?.this" where 0 < h < k: generate an error ("no instance in scope") 4. For expressions like "E?.this": evaluate to O? as follows: 1. If expression occurs in an early construction context of C, then read the synthetic constructor parameter of type E? (we can do this because we are necessarily in a C constructor) 2. Otherwise. read the synthetic field "this$0" 5. For expressions like "E?.this" where h > k as follows: 1. Evaluate "E?.this" per previous steps to get O? 2. Recurse, i.e., evaluate "E?.this" in the context of class E?. This is valid due to the earlier observation. Part 3: When compiling "new C()" (or "C::new") where C is some class having a compiler outer instance: 1. Calculate the type E? - the type of the synthetic parameter O? we need to prepend to the C() constructor invocation 2. Let's assume "new C()" is inside some method, constructor, or initializer of some class T 3. Evaluate "E?.this" in the context of class T (see Part 2) 4. Proceed with construction, prepending the synthetic O? parameter Hopefully this is close to capturing how it "should" work...? -Archie On Wed, Jun 12, 2024 at 7:07?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > Hi, > as we discussed through the details of the new JLS rules in JEP 482, we > have found several issues where javac is emitting incorrect code (e.g. > attempting to capture enclosing instances that are still under > construction). > > In order to allow us to look at these issues more holistically, I've > created a new JBS label, namely "javac-pre-capture". Here's a query to > show them all: > > https://bugs.openjdk.org/issues/?jql=labels%20%3D%20javac-pre-capture > > As stated above, this label is meant for issue related to issues that > have to do broken translation strategy of enclosing instance references. > Such issues can manifest them in different ways: > > * compiler crashes when compiling correct code (e.g. > https://bugs.openjdk.org/browse/JDK-8334037) > * "late" compiler error (e.g. in Lower) as the compiler can't resolve > an enclosing instance (e.g. https://bugs.openjdk.org/browse/JDK-8334121) > * compiler succeds when compiling a correct program, but the compiled > program doesn't verify (we don't have an instance of this, yet, but it's > something bound to occur) > > The scope of this label is somewhat (deliberately) narrow. As such, the > label does NOT cover other issues pertaining to JEP 482, such as > > * compiler attempting to translate a bad program > (https://bugs.openjdk.org/browse/JDK-8334043) > * compiler generating invalid code for reasons that have nothing to do > with capture (https://bugs.openjdk.org/browse/JDK-8332106) > > (if we think that a more general label would be useful to mark all > issues that have to do with pre-construction context, we can do that too). > > Cheers > Maurizio > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Thu Jun 13 10:50:22 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 13 Jun 2024 11:50:22 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> Message-ID: <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> On 12/06/2024 22:06, Archie Cobbs wrote: > Hi Maurizio, > > Thanks for looking into these issues. I agree this is definitely one > of those "we need to rethink and refactor" opportunities. Please let > me know if/how I can help. So far I've done some complaining but not > much else :) > > I'm trying to wrap my head around what's needed and have tried to > write this out in detail mainly for my own understanding. Please tell > me if the below sounds correct to you. > > Observation: this issue doesn't really affect non-static member inner > classes: they can never be declared in an early construction context > of their directly enclosing class. So they always have an immediate > outer instance, and it's always available and usable (or explicitly > provided via qualified new). So all the complexity comes with local > and anonymous classes, especially when they are declared inside early > construction contexts with various levels of nesting. Fortunately, the > JEP treats local and anonymous classes basically the same way. Correct. Btw, I think I like the term "Member inner classes" to mean "non-static". We typically used "nested class" to mean the static kind. In any case, these classes are *not* problematic. Local and anonymous (where the latter is just a special case of the former) are. And the first big issue implementation-wise is that javac is attempting to translate _all_ kinds of inner classes (whether member, or local) in the same way, which leads to issue. > > Some terminology (just placeholders for now, I'm mainly trying to nail > down the logic): > > 1. Every class declaration C has zero or more lexically enclosing > class declarations; let's call them E?, E?, E?... ordered from the > inside out. > 2. For ease of terminology, define the degenerate case E? = C > 3. For each E?, there is possibly an *outer instance* O? *defined* > for C (see below) > 4. For?ease of terminology, define the degenerate case O? = C.this. > 5. When is O? *defined* for C? > 1. O? is always defined > 2. Let k ? 0 be the smallest value such that E? is static (enum, > record,?declared in a static context, etc.) > 3. For all i ? k, the outer instance O? is defined for C and it > has type E? > 4. For all i > k there is no outer instance O? defined for C > 6. If O? is defined, it may also be the case that O? is *accessible* > in C (in non-static contexts): > 1. O? is *accessible* in C if and only if C does not appear in an > early construction context of E? > Correct. The key observation here is that the set of lexically enclosing classes does not map 1:1 with the set of outer instances. > From the above, you can see that the outer classes for which a > corresponding outer instance is defined includes C and is a contiguous > range up until you hit the first static class/context. However, the > set of outer classes for which the corresponding outer instance is > both defined /and accessible/ is an arbitrary subset of that > contiguous range. In effect we create an "inaccessibility hole" at any > class?E? for which C is inside an early construction context of E?. Yep, we sometimes refer to issues like these as "swiss cheese" problem :-) > > Observation: Suppose?O? of type E? is defined for C. Then for any h > > k, O? is defined (accessible) for E? if and only if it is defined > (accessible) for C. In other words, if a class F encloses both C and > E?, then C and E? agree on whether F has a defined and/or accessible > outer instance. This is simply because C and E? are lexically in the > "same place" with respect to F. Yes. > > Define the *compiler outer instance* for C to be that O? for which k > > 0, O? is both defined and accessible, and where k is minimal (if such > a thing exists). Yes, that is a possible strategy to tackle that. There is also another (see below). > > OK now given the above what does the compiler need to do? > > Part 0: The compiler needs to be able to calculate, given any class C, > whether C has a compiler outer instance (let's call it O?) and its > type (let's call it E?) Yup > > Part 1: When compiling some class C for which a compiler outer > instance exists: > > 1. Calculate the type E? > 2. Add a synthetic constructor parameter O? of type E? to each > constructor of C > 3. Add a synthetic field of type E? in which to store O? ("this$n") > 4. Store O? in that field in each constructor of C (very first thing) > Yes. Step (3) is done "on-demand", but essence is the same. > Part 2: Compiling the expression "Foo.this" in the context of some > class C: > > 1. If C has no compiler outer instance, then error unless Foo = C. > 2. Otherwise let O? with type E? be the compiler outer instance for C > 3. For expressions like "E?.this" where 0 < h < k: generate an error > ("no instance in scope") > 4. For expressions like "E?.this": evaluate to O? as follows: > 1. If expression occurs in an early construction context of C, > then read the synthetic constructor parameter of type E? (we > can do this because we are necessarily in a C constructor) > 2. Otherwise. read the synthetic field "this$0" > 5. For expressions like "E?.this" where h > k as follows: > 1. Evaluate "E?.this" per previous steps to get O? > 2. Recurse, i.e., evaluate "E?.this" in the context of class E?. > This is valid due to the earlier observation. > Yes - note the slight complication in step 4 due to the fact that accessing the field might not always be possible. > Part 3: When compiling "new C()" (or "C::new") where C is some class > having a compiler outer instance: > > 1. Calculate the type E? - the type of the synthetic parameter O? we > need to prepend to the C() constructor invocation > 2. Let's assume "new C()" is inside some method, constructor, or > initializer of some class T > 3. Evaluate "E?.this" in the context of class T (see Part 2) > 4. Proceed with construction, prepending the synthetic O? parameter > > Hopefully this is close to capturing how it "should" work...? Yes. This is one way to do it. Another way to do it would be to say that local/anonymous class do not get a blessed "compiler outer instance", period. Instead, they just capture all the enclosing "this" they need in order to get the job done, without the expectation that one enclosing this will be reachable from the other. This is based on the observation that there's really two ways to get to the same result: * Lower has this mechanism, called "outerThisStack" which more or less tracks what you have called "compiler outer instance". E.g. at given point in the code, what enclosing instances are available to me? * But there's also another mechanism - that for captured values (Lower calls this "proxies"). This is yet another set of values that are accessible to the local/anon class via a field (or a constructor parameter, if inside a constructor) The approach you described tries to tweak "outerThisStack" so that the stack contains the right bits (and skips over inaccessible enclosing instances). The alternate approach is to just ditch "outerThisStack" for local/anon classes, and switch to a more capture-oriented translation. Note that lambda translation (LambdaToMethod) is more in the latter camp - e.g. if a lambda occurs in a pre-construction context, and needs enclosing instances O1, O2, O3, then it will accept _all_ of them as captured parameters. There's also a second, more subtle, implementation issue, which I'm in the process of evaluating as we speak. It seems to me that the ordering of the compiler steps Lower and LambdaToMethod is backwards. E.g. LambdaToMethod goes first, then we translate inner classes with Lower. But this leads to issues: the lambda translation code doesn't see the full picture as local classes have not been translated yet. So it basically has to "guess" which variables will need to be captured for local classes inside the lambda to work. This is not great, and leads to bugs that are very difficult to solve, such as this: https://bugs.openjdk.org/browse/JDK-8334037 I'm currently doing some experiments to see if we can move the compiler phases the "right way" (but this is a big change, and not something we can do for 23). My working theory is that if we (a) fix translation of local classes as described above (using either approaches) AND (b) move LambdaToMethod _after_ Lower, then most of the pesky translation issues with pre-construction context should disappear. Maurizio > > -Archie > > On Wed, Jun 12, 2024 at 7:07?AM Maurizio Cimadamore > wrote: > > Hi, > as we discussed through the details of the new JLS rules in JEP > 482, we > have found several issues where javac is emitting incorrect code > (e.g. > attempting to capture enclosing instances that are still under > construction). > > In order to allow us to look at these issues more holistically, I've > created a new JBS label, namely "javac-pre-capture". Here's a > query to > show them all: > > https://bugs.openjdk.org/issues/?jql=labels%20%3D%20javac-pre-capture > > As stated above, this label is meant for issue related to issues that > have to do broken translation strategy of enclosing instance > references. > Such issues can manifest them in different ways: > > * compiler crashes when compiling correct code (e.g. > https://bugs.openjdk.org/browse/JDK-8334037) > * "late" compiler error? (e.g. in Lower) as the compiler can't > resolve > an enclosing instance (e.g. > https://bugs.openjdk.org/browse/JDK-8334121) > * compiler succeds when compiling a correct program, but the compiled > program doesn't verify (we don't have an instance of this, yet, > but it's > something bound to occur) > > The scope of this label is somewhat (deliberately) narrow. As > such, the > label does NOT cover other issues pertaining to JEP 482, such as > > * compiler attempting to translate a bad program > (https://bugs.openjdk.org/browse/JDK-8334043) > * compiler generating invalid code for reasons that have nothing > to do > with capture (https://bugs.openjdk.org/browse/JDK-8332106) > > (if we think that a more general label would be useful to mark all > issues that have to do with pre-construction context, we can do > that too). > > Cheers > Maurizio > > > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Jun 13 14:31:04 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 13 Jun 2024 09:31:04 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> Message-ID: Hi Maurizio, On Thu, Jun 13, 2024 at 5:50?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > There's also a second, more subtle, implementation issue, which I'm in the > process of evaluating as we speak. It seems to me that the ordering of the > compiler steps Lower and LambdaToMethod is backwards. > Ah! That helps clarify things for me. Thanks for your analysis. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Thu Jun 13 17:05:10 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 13 Jun 2024 18:05:10 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> Message-ID: On 13/06/2024 15:31, Archie Cobbs wrote: > Hi Maurizio, > > On Thu, Jun 13, 2024 at 5:50?AM Maurizio Cimadamore > wrote: > > There's also a second, more subtle, implementation issue, which > I'm in the process of evaluating as we speak. It seems to me that > the ordering of the compiler steps Lower and LambdaToMethod is > backwards. > > > Ah! That helps clarify things for me. Thanks for your analysis. > > -Archie > > -- > Archie L. Cobbs Here's a first draft of the work to move LambdaToMethod after Lower: https://github.com/mcimadamore/jdk/pull/new/cleanup_capture Things went considerably smooth, given the depth of the surgery. Some notable facts: 1. now LambdaToMethod deals with separate classes, not a single toplevel one (as it now occurs _after_ lowering) 2. Lower needs a to override `visitLambda`, so that it can properly lower the lambda return expressions 3. The code to "desugars" complex method references into lambdas has been moved from LambdaToMethod to Lower (so that we can fixup references to inner classes etc.) 4. The method reference receiver of desugared lambda expression is now stashed into the JCLambda AST (as LambdaToMethod will need that later) 5. the lambda deserialization method generated by LambdaToMethod contains string in switch, and implicitly requires boxing, so we need to explictly lower that 6. some code generated by Lower is not stable, so lambda deduplication fails in new ways The nice thing about this cleanup is that a ton of questionable stuff just disappears: * no need to have a "lambdaTranslationMap" in Lower. This was only needed when a lambda expression anticipated capture later required from Lower but that did not appear in the source code (yet) * no need to have "typesUnderConstruction" (and associated visitors) in LambdaToMethod. As now lambda translation occurs _after_ inner class translation, LambdaToMethod can expect that references to enclosing instances have already been figured out by Lower * no more need to "guess" captured values for local classes inside a lambda - the required captured values are now made explicit in the AST. This means we don't need to run a modified FreeVarCollector inside LambdaToMethod All this means is that stuff like this: https://bugs.openjdk.org/browse/JDK-8334037 Just works. And, if we make improvements in Lower to fix the issues with local and anonymous classes and inaccessible enclosing instances, then LambdaToMethod will work w/o any manual adjustment required. Of course, this is a biggie piece of work, so I don't think we should do it for 23, as we're already past RDP1. More testing is also needed. But, a promising start. Cheers Maurizio -------------- next part -------------- An HTML attachment was scrubbed... URL: From vicente.romero at oracle.com Thu Jun 13 17:20:28 2024 From: vicente.romero at oracle.com (Vicente Romero) Date: Thu, 13 Jun 2024 13:20:28 -0400 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> Message-ID: <91f4130d-1583-4f25-b71d-c9d68957bb3e@oracle.com> really nice piece of work and badly needed, this area has been a source of nasty bugs for a while now. This will be a great move to reduce them and will add a solid base to fix future bugs in this area, Vicente On 6/13/24 13:05, Maurizio Cimadamore wrote: > > > On 13/06/2024 15:31, Archie Cobbs wrote: >> Hi Maurizio, >> >> On Thu, Jun 13, 2024 at 5:50?AM Maurizio Cimadamore >> wrote: >> >> There's also a second, more subtle, implementation issue, which >> I'm in the process of evaluating as we speak. It seems to me that >> the ordering of the compiler steps Lower and LambdaToMethod is >> backwards. >> >> >> Ah! That helps clarify things for me. Thanks for your analysis. >> >> -Archie >> >> -- >> Archie L. Cobbs > > Here's a first draft of the work to move LambdaToMethod after Lower: > > https://github.com/mcimadamore/jdk/pull/new/cleanup_capture > > Things went considerably smooth, given the depth of the surgery. Some > notable facts: > > 1. now LambdaToMethod deals with separate classes, not a single > toplevel one (as it now occurs _after_ lowering) > 2. Lower needs a to override `visitLambda`, so that it can properly > lower the lambda return expressions > 3. The code to "desugars" complex method references into lambdas has > been moved from LambdaToMethod to Lower (so that we can fixup > references to inner classes etc.) > 4. The method reference receiver of desugared lambda expression is now > stashed into the JCLambda AST (as LambdaToMethod will need that later) > 5. the lambda deserialization method generated by LambdaToMethod > contains string in switch, and implicitly requires boxing, so we need > to explictly lower that > 6. some code generated by Lower is not stable, so lambda deduplication > fails in new ways > > The nice thing about this cleanup is that a ton of questionable stuff > just disappears: > > * no need to have a "lambdaTranslationMap" in Lower. This was only > needed when a lambda expression anticipated capture later required > from Lower but that did not appear in the source code (yet) > * no need to have "typesUnderConstruction" (and associated visitors) > in LambdaToMethod. As now lambda translation occurs _after_ inner > class translation, LambdaToMethod can expect that references to > enclosing instances have already been figured out by Lower > * no more need to "guess" captured values for local classes inside a > lambda - the required captured values are now made explicit in the > AST. This means we don't need to run a modified FreeVarCollector > inside LambdaToMethod > > All this means is that stuff like this: > > https://bugs.openjdk.org/browse/JDK-8334037 > > Just works. And, if we make improvements in Lower to fix the issues > with local and anonymous classes and inaccessible enclosing instances, > then LambdaToMethod will work w/o any manual adjustment required. > > Of course, this is a biggie piece of work, so I don't think we should > do it for 23, as we're already past RDP1. More testing is also needed. > But, a promising start. > > Cheers > Maurizio > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Jun 13 18:36:07 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 13 Jun 2024 13:36:07 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> Message-ID: Refactoring Awesomeness Ratio (RAR) = # lines removed / # lines added. Congrats on an RAR of 1.59 :) -Archie On Thu, Jun 13, 2024 at 12:05?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > Here's a first draft of the work to move LambdaToMethod after Lower: > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Thu Jun 13 20:34:48 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 13 Jun 2024 21:34:48 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> Message-ID: <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> Heh. Thanks :-) So, I think this patch can be used as a basis for further hacking in Lower (e.g. to deal with inaccessible enclosing instances the right way). You seemed to have put some thought into how to do it, so if you want to go ahead that's fine by me (if not, that's fine too, but let's not duplicate work). Cheers Maurizio On 13/06/2024 19:36, Archie Cobbs wrote: > Refactoring Awesomeness Ratio (RAR) = # lines removed / # lines added. > > Congrats on an RAR of 1.59 :) > > -Archie > > On Thu, Jun 13, 2024 at 12:05?PM Maurizio Cimadamore > wrote: > > Here's a first draft of the work to move LambdaToMethod after Lower: > > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jun 14 12:57:14 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 14 Jun 2024 07:57:14 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> Message-ID: I'm done with my hacking for now (some of which your changes subsume e.g. JDK-8334252) - and I'm sure I'll want to study your improvements for a while before trying anything new. Thanks! On Thu, Jun 13, 2024 at 3:34?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > Heh. Thanks :-) > > So, I think this patch can be used as a basis for further hacking in Lower > (e.g. to deal with inaccessible enclosing instances the right way). > > You seemed to have put some thought into how to do it, so if you want to > go ahead that's fine by me (if not, that's fine too, but let's not > duplicate work). > > Cheers > Maurizio > > > On 13/06/2024 19:36, Archie Cobbs wrote: > > Refactoring Awesomeness Ratio (RAR) = # lines removed / # lines added. > > Congrats on an RAR of 1.59 :) > > -Archie > > On Thu, Jun 13, 2024 at 12:05?PM Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > >> Here's a first draft of the work to move LambdaToMethod after Lower: >> > > -- > Archie L. Cobbs > > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 14 14:53:20 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 14 Jun 2024 15:53:20 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> Message-ID: <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> Ok. This is an attempt I made to fix the enclosing instance issue. It sits on top of the patch I shared yestreday, but probably could be integrated independently: https://github.com/mcimadamore/jdk/compare/cleanup_capture...mcimadamore:jdk:cleanup_capture+encl_fix?expand=1 The core of the fix is the addition of a new function in `Symbol`, namely `Symbol::innermostAccessibleEnclosingClass`. As the name implies, the goal of this function is to return the class symbol corresponding to the innermost enclosing type that is accessible from the symbol's class. This is used by `Lower` to determine the type of `this$0`, which allows us to construct a chain of enclosing instances that skips over all the inaccessible types. I've also updated the `Symbol::hasOuterInstance` method, so that it, too, will skip over inaccessible enclosing type (and only return `true` if some accessible enclosing type is found). Again, the cleanup is rather nice. Note how much simpler `Lower.FreeVarCollector` now is, as we no longer have to do heroics to treat enclosing instances as captured variables (or *freevars* using `Lower`'s lingo). As a result, `FreeVarCollector` is now effectively stateless - it no longer depends on the contents of `outerThisStack`. As such, it is now easier for clients to predict which symbols will be captured by a given local class, as this logic now is effectively independent from `Lower` (!!). This seems to solve issues like: https://bugs.openjdk.org/browse/JDK-8334121 And, together with the patch I shared yesterday, should put a lid on the myriad of translation bugs associated with local classes/lambdas in pre-construction contexts. Cheers Maurizio On 14/06/2024 13:57, Archie Cobbs wrote: > I'm done with my hacking for now (some of which your changes subsume > e.g. JDK-8334252) - and I'm sure I'll want to study your improvements > for a while before trying anything new. > > Thanks! > > On Thu, Jun 13, 2024 at 3:34?PM Maurizio Cimadamore > wrote: > > Heh. Thanks :-) > > So, I think this patch can be used as a basis for further hacking > in Lower (e.g. to deal with inaccessible enclosing instances the > right way). > > You seemed to have put some thought into how to do it, so if you > want to go ahead that's fine by me (if not, that's fine too, but > let's not duplicate work). > > Cheers > Maurizio > > > On 13/06/2024 19:36, Archie Cobbs wrote: >> Refactoring Awesomeness Ratio (RAR) = # lines removed / # lines >> added. >> >> Congrats on an RAR of 1.59 :) >> >> -Archie >> >> On Thu, Jun 13, 2024 at 12:05?PM Maurizio Cimadamore >> wrote: >> >> Here's a first draft of the work to move LambdaToMethod after >> Lower: >> >> >> -- >> Archie L. Cobbs > > > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jun 14 17:04:01 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 14 Jun 2024 12:04:01 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> Message-ID: Another nice cleanup ... RAR = 2.29!! Combining this patch with your first patch and the other PR's already submitted fixes all the test cases I've been playing with. Your change to the semantics of hasOuterInstance() happens to invalidate my fix for JDK-8334248 but that's OK, the new semantics are more correct and I've rewritten my fix to instead check for NOOUTERTHIS directly. -Archie On Fri, Jun 14, 2024 at 9:53?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > Ok. > > This is an attempt I made to fix the enclosing instance issue. It sits on > top of the patch I shared yestreday, but probably could be integrated > independently: > > > https://github.com/mcimadamore/jdk/compare/cleanup_capture...mcimadamore:jdk:cleanup_capture+encl_fix?expand=1 > > The core of the fix is the addition of a new function in `Symbol`, namely > `Symbol::innermostAccessibleEnclosingClass`. As the name implies, the goal > of this function is to return the class symbol corresponding to the > innermost enclosing type that is accessible from the symbol's class. > > This is used by `Lower` to determine the type of `this$0`, which allows us > to construct a chain of enclosing instances that skips over all the > inaccessible types. > > I've also updated the `Symbol::hasOuterInstance` method, so that it, too, > will skip over inaccessible enclosing type (and only return `true` if some > accessible enclosing type is found). > > Again, the cleanup is rather nice. Note how much simpler > `Lower.FreeVarCollector` now is, as we no longer have to do heroics to > treat enclosing instances as captured variables (or *freevars* using > `Lower`'s lingo). As a result, `FreeVarCollector` is now effectively > stateless - it no longer depends on the contents of `outerThisStack`. As > such, it is now easier for clients to predict which symbols will be > captured by a given local class, as this logic now is effectively > independent from `Lower` (!!). > > This seems to solve issues like: > https://bugs.openjdk.org/browse/JDK-8334121 > > And, together with the patch I shared yesterday, should put a lid on the > myriad of translation bugs associated with local classes/lambdas in > pre-construction contexts. > > Cheers > Maurizio > On 14/06/2024 13:57, Archie Cobbs wrote: > > I'm done with my hacking for now (some of which your changes subsume e.g. > JDK-8334252) - and I'm sure I'll want to study your improvements for a > while before trying anything new. > > Thanks! > > On Thu, Jun 13, 2024 at 3:34?PM Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > >> Heh. Thanks :-) >> >> So, I think this patch can be used as a basis for further hacking in >> Lower (e.g. to deal with inaccessible enclosing instances the right way). >> >> You seemed to have put some thought into how to do it, so if you want to >> go ahead that's fine by me (if not, that's fine too, but let's not >> duplicate work). >> >> Cheers >> Maurizio >> >> >> On 13/06/2024 19:36, Archie Cobbs wrote: >> >> Refactoring Awesomeness Ratio (RAR) = # lines removed / # lines added. >> >> Congrats on an RAR of 1.59 :) >> >> -Archie >> >> On Thu, Jun 13, 2024 at 12:05?PM Maurizio Cimadamore < >> maurizio.cimadamore at oracle.com> wrote: >> >>> Here's a first draft of the work to move LambdaToMethod after Lower: >>> >> >> -- >> Archie L. Cobbs >> >> > > -- > Archie L. Cobbs > > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 14 17:33:06 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 14 Jun 2024 18:33:06 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> Message-ID: On 14/06/2024 18:04, Archie Cobbs wrote: > Your change to the semantics of hasOuterInstance() happens to > invalidate my fix for JDK-8334248 > > but that's OK, the new semantics are more correct and I've rewritten > my fix to instead check for NOOUTERTHIS directly. Thanks. I wonder... is the real problem here that Resolve::resolveImplicitThis doesn't skip over classes that have NOOUTERTHIS set? E.g. your patch just disables the check if we see NOOUTERTHIS... which is ok if the current class happens to have NO accessible enclosing instances. But what if there's *some* accessible enclosing instance? Something like this: ```java class Outer { ??? void m() { } ??? class Inner { ??????? Inner() { ??????????? class Foo { void g() { m(); } } ??????????? super(); ??????????? class Bar { static void r() { new Foo(); } }; ??????? } ??? } } ``` Btw, it seems like this code crashes javac (even with both patches applies). I don't think this code should be allowed at all (you can't create the local Foo from the static Bar::r). So, this is related to the bug you mentioned, and I believe we need checks for the innermost accessible enclosing instance here. Maurizio -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 14 17:43:48 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 14 Jun 2024 18:43:48 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> Message-ID: <3c67a2f5-0fc0-4740-ae2d-d729c8eea2ed@oracle.com> Heh, yes. Look at this code in Attr::visitNewClass: |} else if (!clazztype.tsym.isInterface() && (clazztype.tsym.flags_field & NOOUTERTHIS) == 0 && clazztype.getEnclosingType().hasTag(CLASS)) { // Check for the existence of an apropos outer instance rs.resolveImplicitThis(tree.pos(), env, clazztype); } | This is basically the same as your new fix. And it doesn?t work in the case I presented. Maurizio On 14/06/2024 18:33, Maurizio Cimadamore wrote: > > On 14/06/2024 18:04, Archie Cobbs wrote: >> Your change to the semantics of hasOuterInstance() happens to >> invalidate my fix for JDK-8334248 >> >> but that's OK, the new semantics are more correct and I've rewritten >> my fix to instead check for NOOUTERTHIS directly. > > Thanks. I wonder... is the real problem here that > Resolve::resolveImplicitThis doesn't skip over classes that have > NOOUTERTHIS set? E.g. your patch just disables the check if we see > NOOUTERTHIS... which is ok if the current class happens to have NO > accessible enclosing instances. But what if there's *some* accessible > enclosing instance? > > Something like this: > > ```java > class Outer { > ??? void m() { } > > ??? class Inner { > ??????? Inner() { > ??????????? class Foo { void g() { m(); } } > ??????????? super(); > ??????????? class Bar { static void r() { new Foo(); } }; > ??????? } > ??? } > } > > ``` > > Btw, it seems like this code crashes javac (even with both patches > applies). I don't think this code should be allowed at all (you can't > create the local Foo from the static Bar::r). So, this is related to > the bug you mentioned, and I believe we need checks for the innermost > accessible enclosing instance here. > > Maurizio > > > ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jun 14 17:51:35 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 14 Jun 2024 12:51:35 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> Message-ID: On Fri, Jun 14, 2024 at 12:33?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > On 14/06/2024 18:04, Archie Cobbs wrote: > > Your change to the semantics of hasOuterInstance() happens to invalidate > my fix for JDK-8334248 > > but that's OK, the new semantics are more correct and I've rewritten my fix > to instead check for NOOUTERTHIS directly. > > Thanks. I wonder... is the real problem here that > Resolve::resolveImplicitThis doesn't skip over classes that have > NOOUTERTHIS set? E.g. your patch just disables the check if we see > NOOUTERTHIS... which is ok if the current class happens to have NO > accessible enclosing instances. But what if there's *some* accessible > enclosing instance? > My current understanding is that my fix is correct because the compiler checks for what it calls an "implicit this" - which we would describe as an "immediate enclosing instance" - separately from non-immediate enclosing instances. The reason I say this is that before I made this adjustment (from checking hasOuterInstance() ? NOOUTERTHIS == 0) the following program failed to compile: $ cat EarlyLocalTest3.java public class EarlyLocalTest3 { class Test { Test() { class InnerLocal { } Runnable r = InnerLocal::new; r.run(); super(); } } } $ javac -enable-preview --release 24 EarlyLocalTest3.java ../test/langtools/tools/javac/SuperInit/EarlyLocalTest3.java:6: error: cannot reference this before supertype constructor has been called Runnable r = InnerLocal::new; ^ The compiler is detecting that InnerLocal has an outer instance ( EarlyLocalTest3) and throwing an error, but that's incorrect because the ( *non-immediate*) outer instance for EarlyLocalTest3 is actually accessible at that location. Btw, it seems like this code crashes javac (even with both patches > applies). I don't think this code should be allowed at all (you can't > create the local Foo from the static Bar::r). So, this is related to the > bug you mentioned, and I believe we need checks for the innermost > accessible enclosing instance here. > So yes I also see the compiler crashing on that example but I think that may be separate from the issue above...? I don't fully understand this one yet. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 14 17:56:44 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 14 Jun 2024 18:56:44 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> Message-ID: <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> On 14/06/2024 18:51, Archie Cobbs wrote: > So yes I also see the compiler crashing on that example but I think > that may be separate from the issue above...? I don't fully understand > this one yet. I left an example of code that might defeat your fix in the PR. https://github.com/openjdk/jdk/pull/19705#issuecomment-2168497505 Maurizio From archie.cobbs at gmail.com Fri Jun 14 19:49:53 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 14 Jun 2024 14:49:53 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> Message-ID: Thanks. Let's transition this particular discussion to that PR. I added a rebuttal for you :) https://github.com/openjdk/jdk/pull/19705 -Archie On Fri, Jun 14, 2024 at 12:56?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 14/06/2024 18:51, Archie Cobbs wrote: > > So yes I also see the compiler crashing on that example but I think > > that may be separate from the issue above...? I don't fully understand > > this one yet. > > I left an example of code that might defeat your fix in the PR. > > https://github.com/openjdk/jdk/pull/19705#issuecomment-2168497505 > > Maurizio > > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Thu Jun 20 15:15:52 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 20 Jun 2024 16:15:52 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> Message-ID: Hi, I have put together a patch that overhauls the attribution checks for local and member inner class creation: https://github.com/openjdk/jdk/compare/master...mcimadamore:jdk:refactor_encl_checks?expand=1 This was far more complex than I expected, as I faced issues not only in the implementation, but also in the JLS (see below). Let me attempt to summarize the rationale behind this fix. Let?s start with member inner classes. Whenever we see |new M()| where |M| is a member inner class, we need to /infer/ an expression for |M|?s enclosing instance, given none is provided. This inference problem is also present when checking a |super(...)| constructor call: if the superclass is a member inner class |M|, then validating the constructor call implies /inferring/ a suitable enclosing instance for |M|, as if we were checking |new M()|. How should this inference process work? Well, it should look at /all/ the enclosing instances available to us, and pick the innermost enclosing instance of type |C| such that: * |C.this| is accessible (e.g. not in |C|?s early construction context) and; * |C| is a subclass of |M|?s enclosing class. The crucial observation here is that there can be /multiple/ valid enclosing instances, and the innermost one might not be available due to early construction context, so we need to be able to skip that, and jump to the next. Consider this example: |class Outer { class InnerSuperclass { } static class InnerOuter extends Outer { class InnerInnerOuter extends Outer { InnerInnerOuter() { class InnerSubclass extends InnerSuperclass { InnerSubclass() { super(); // (1) } } super(); } } } } | This program doesn?t compile today. But should it? Yes: while it?s true that |InnerInnerOuter.this| cannot be accessed in (1), |InnerOuter.this| can, and that?s also a suitable enclosing instance (as |InnerOuter extends Outer|). What about local classes? Should they undergo a similar check? The answer is no, as 15.9.2 is silent about this. What matters, for local classes, is that the local class creation expression occurs in a context where we can access local variables defined in the method in which the local class is defined. With the new checks in place we can finally solve both: * https://bugs.openjdk.org/browse/JDK-8334248 and * https://bugs.openjdk.org/browse/JDK-8322882 Few notes: * |Lower| needs some updates to support programs like the the one above. That is, we have to teach |Lower| that the current class? |this| cannot be touched if the class is in early construction context; * the JLS text for member inner class creation seems lacking (in both 15.9.2 and in 8.8.7.1). First, the JLS fails to recognize that a /subclass/ of the enclosing instance is still a suitable value for constructing the member class. Secondly, the JLS doesn?t seem to take early construction contexts into account (so might end up picking the wrong thing, or reporting an unnecessary error); * the JLS text for local inner class creation in 15.9.2 seems also lacking. The check for local classes seems too weak, which leaves out intersting cases such as JDK-8322882. I think that this fix, coupled with the translation fixes I shared last week should implement JEP 482 ?as intended? (of course, for some of the fixes described here, some JLS udpate is also required, as these are not ?just? compiler bugs). Cheers Maurizio On 14/06/2024 20:49, Archie Cobbs wrote: Thanks. Let?s transition this particular discussion to that PR. I added a rebuttal for you :) https://github.com/openjdk/jdk/pull/19705 https://urldefense.com/v3/__https://github.com/openjdk/jdk/pull/19705__;!!ACWV5N9M2RV99hQ!IPTJtMcgd06MLFxVOwPycDr63fpHIABTrno8GoNywuev7W-GybSntWK-JnxR6AxfqkAm2yp8w79BYUoeki12fBoD2It17g$ -Archie On Fri, Jun 14, 2024 at 12:56?PM Maurizio Cimadamore maurizio.cimadamore at oracle.com wrote: On 14/06/2024 18:51, Archie Cobbs wrote: So yes I also see the compiler crashing on that example but I think that may be separate from the issue above?? I don?t fully understand this one yet. I left an example of code that might defeat your fix in the PR. https://github.com/openjdk/jdk/pull/19705#issuecomment-2168497505 https://urldefense.com/v3/__https://github.com/openjdk/jdk/pull/19705*issuecomment-2168497505__;Iw!!ACWV5N9M2RV99hQ!IPTJtMcgd06MLFxVOwPycDr63fpHIABTrno8GoNywuev7W-GybSntWK-JnxR6AxfqkAm2yp8w79BYUoeki12fBpkHh3GXQ$ Maurizio ? Archie L. Cobbs ? -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Jun 20 16:43:46 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 20 Jun 2024 11:43:46 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> Message-ID: On Thu, Jun 20, 2024 at 10:16?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > The crucial observation here is that there can be *multiple* valid > enclosing instances, and the innermost one might not be available due to > early construction context, so we need to be able to skip that, and jump to > the next > Hah, that's a new one for me... :) Just curious, your comment "the JLS text for member inner class creation seems lacking" makes me wonder if this obscure corner already existed (i.e., before "flexible constructors") with the anonymous class equivalent: class Outer2 { Outer2() { } Outer2(Object obj) { } class InnerSuperclass { { System.out.println(Outer2.this); } } static class InnerOuter extends Outer2 { class InnerInnerOuter extends Outer2 { InnerInnerOuter() { super(new InnerSuperclass() { }); } } } public static void main(String[] args) { new InnerOuter().new InnerInnerOuter(); // prints "Outer2$InnerOuter at 327471b5" } } That program doesn't compile now but by the same argument maybe it should - or at least the same ambiguity already exists? I think that this fix, coupled with the translation fixes I shared last > week should implement JEP 482 ?as intended? > Awesome, thanks for tackling this! Fyi, I've merged this patch into my javac-pre-capture-fixes branch (already includes your lambda refactoring). -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Thu Jun 20 17:03:58 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 20 Jun 2024 18:03:58 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> Message-ID: <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> On 20/06/2024 17:43, Archie Cobbs wrote: > On Thu, Jun 20, 2024 at 10:16?AM Maurizio Cimadamore > wrote: > > The crucial observation here is that there can be /multiple/ valid > enclosing instances, and the innermost one might not be available > due to early construction context, so we need to be able to skip > that, and jump to the next > > Hah, that's a new one for me... :) > > Just curious, your comment "the JLS text for member inner class > creation seems lacking" makes me wonder if this obscure corner already > existed (i.e., before "flexible constructors") with the anonymous > class equivalent: > |class Outer2 { ? ? Outer2() { ? ? } ? ? Outer2(Object obj) { ? ? } ? > ? class InnerSuperclass { ? ? ? ? { System.out.println(Outer2.this); } > ? ? } ? ? static class InnerOuter extends Outer2 { ? ? ? ? class > InnerInnerOuter extends Outer2 { ? ? ? ? ? ? InnerInnerOuter() { ? ? ? > ? ? ? ? ? super(new InnerSuperclass() { }); ? ? ? ? ? ? } ? ? ? ? } ? > ? } ? ? public static void main(String[] args) { ? ? ? ? new > InnerOuter().new InnerInnerOuter(); // prints > "Outer2$InnerOuter at 327471b5" ? ? } } | > That program doesn't compile now but by the same argument maybe it > should - or at least the same ambiguity already exists? The issues are pre-existing, although, w/o JEP 482, the anon InnerSuperClass would be created in a static context, so it kind of follows that there's no enclosing instance available there. > > I think that this fix, coupled with the translation fixes I shared > last week should implement JEP 482 ?as intended? > > Awesome, thanks for tackling this! Fyi, I've merged this patch into my > javac-pre-capture-fixes > > branch (already includes your lambda refactoring). Let me know if that fixes all the issue we're seeing (it would be great if we could collect all test cases in some github repo, so that we can add to them easily). Maurizio > > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Jun 20 18:54:08 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 20 Jun 2024 13:54:08 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <6494cf25-4cfb-4bc1-8462-7ded0b90830d@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> Message-ID: On Thu, Jun 20, 2024 at 12:04?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > I think that this fix, coupled with the translation fixes I shared last >> week should implement JEP 482 ?as intended? >> > Awesome, thanks for tackling this! Fyi, I've merged this patch into my > javac-pre-capture-fixes > > branch (already includes your lambda refactoring). > > Let me know if that fixes all the issue we're seeing (it would be great if > we could collect all test cases in some github repo, so that we can add to > them easily). > It fixes all the tests I've been running - these tests are already being collected in the aforementioned github branch. And I just added a couple more, so that now all of the "javac-pre-capture" issues are represented with tests, and they are all passing. So we're looking good! Let me know how you'd like to proceed and if I can help in any way. A few housekeeping notes about the current "javac-pre-capture" branch: - I fixed a typo in your "canRefThis" comment in Lower.java ("is" ? "if") - The fix for JDK-8334252 (already committed) had the wrong bug # in the unit test (now fixed - does this need a separate bug?) - We may want a new error message for the JDK-8322882 error, which e.g. now reports "non-static method foo(Object) cannot be referenced from a static context" for the test case even though foo() is a static method. Thanks, -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Thu Jun 20 21:03:10 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 20 Jun 2024 22:03:10 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> Message-ID: On 20/06/2024 19:54, Archie Cobbs wrote: > On Thu, Jun 20, 2024 at 12:04?PM Maurizio Cimadamore > wrote: > >> I think that this fix, coupled with the translation fixes I >> shared last week should implement JEP 482 ?as intended? >> >> Awesome, thanks for tackling this! Fyi, I've merged this patch >> into my javac-pre-capture-fixes >> >> branch (already includes your lambda refactoring). > > Let me know if that fixes all the issue we're seeing (it would be > great if we could collect all test cases in some github repo, so > that we can add to them easily). > > It fixes all the tests I've been running - these tests are already > being collected in the aforementioned github branch. > > And I just added a couple more, so that now all of the > "javac-pre-capture" issues are represented with tests, and they are > all passing. Thanks for checking. > > So we're looking good! Let me know how you'd like to proceed and if I > can help in any way. I think we need to start to roll in the patches. I think there's a dependency between the two translation patches I shared last week, whereas this latest one seems more independent. I believe it would probably be best to start from the LambdaToMethod move, as that's probably the biggest change. > > A few housekeeping notes about the current "javac-pre-capture" branch: > > * I fixed a typo in your "canRefThis" comment in Lower.java ("is" ? > "if") > Thanks > > * The fix for JDK-8334252 (already committed) had the wrong bug # in > the unit test (now fixed - does this need a separate bug?) > Yes, my feeling is that it would be better to file more general bugs and fix those, and then in the PR say that the fix also fixes the other bugs we know about (there's a Skara command for that, "\solves"). > > * We may want a new error message for the JDK-8322882 error, which > e.g. now reports "non-static method foo(Object) cannot be > referenced from a static context" for the test case even though > foo() is a static method. > Yeah, error messages are all equally bad. I think they should be addressed in a separate and follow up PR from the ones we discussed. So, to recap, we need 4 issues: 1. move LambdaToMethod after Lower 2. fix Lower so that it doesn't include inaccessible enclosing instances in the this$xyz chain 3. fix the frontend, so that type-checking of instance creation expression behaves as expected 4. do a final pass on the error messages Maurizio > Thanks, > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Thu Jun 20 21:50:47 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Thu, 20 Jun 2024 16:50:47 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> Message-ID: On Thu, Jun 20, 2024 at 4:03?PM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > So, to recap, we need 4 issues: > > 1. move LambdaToMethod after Lower > 2. fix Lower so that it doesn't include inaccessible enclosing instances > in the this$xyz chain > 3. fix the frontend, so that type-checking of instance creation expression > behaves as expected > 4. do a final pass on the error messages > 0.5. Wrong bug number in regression test for JDK-8334252 (just created as JDK-8334679 ) Thanks, -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 21 14:26:19 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 21 Jun 2024 15:26:19 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> Message-ID: Actually, on a second thought, let's just reuse the bugs we have for these issues - e.g. 1. https://bugs.openjdk.org/browse/JDK-8334037 2. https://bugs.openjdk.org/browse/JDK-8334121 3. https://bugs.openjdk.org/browse/JDK-8334248 If that's ok, I'd suggest to withdraw the PR for (3), and assign that issue to me. Cheers Maurizio On 20/06/2024 22:50, Archie Cobbs wrote: > On Thu, Jun 20, 2024 at 4:03?PM Maurizio Cimadamore > wrote: > > So, to recap, we need 4 issues: > > 1. move LambdaToMethod after Lower > 2. fix Lower so that it doesn't include inaccessible enclosing > instances in the this$xyz chain > 3. fix the frontend, so that type-checking of instance creation > expression behaves as expected > 4. do a final pass on the error messages > > 0.5.?Wrong bug number in regression test for JDK-8334252 (just created > as JDK-8334679 ) > > Thanks, > -Archie > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Jun 21 15:12:28 2024 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 21 Jun 2024 10:12:28 -0500 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <52f9e05f-8c28-46dd-8c90-8361c50de220@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> Message-ID: Sounds good - thanks. On Fri, Jun 21, 2024 at 9:26?AM Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > Actually, on a second thought, let's just reuse the bugs we have for these > issues - e.g. > > 1. https://bugs.openjdk.org/browse/JDK-8334037 > 2. https://bugs.openjdk.org/browse/JDK-8334121 > 3. https://bugs.openjdk.org/browse/JDK-8334248 > > If that's ok, I'd suggest to withdraw the PR for (3), and assign that > issue to me. > > Cheers > Maurizio > On 20/06/2024 22:50, Archie Cobbs wrote: > > On Thu, Jun 20, 2024 at 4:03?PM Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > >> So, to recap, we need 4 issues: >> >> 1. move LambdaToMethod after Lower >> 2. fix Lower so that it doesn't include inaccessible enclosing instances >> in the this$xyz chain >> 3. fix the frontend, so that type-checking of instance creation >> expression behaves as expected >> 4. do a final pass on the error messages >> > 0.5. Wrong bug number in regression test for JDK-8334252 (just created as > JDK-8334679 ) > > Thanks, > -Archie > > -- > Archie L. Cobbs > > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jun 21 17:44:55 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 21 Jun 2024 18:44:55 +0100 Subject: known translation issues related to JEP 482 In-Reply-To: References: <761a301f-e5e6-4713-b63b-57adb73b90f3@oracle.com> <2b5174d2-f3bb-40aa-8a90-5329c809968e@oracle.com> <01012f20-e992-4607-b5a9-cb70b63cc695@oracle.com> <38347674-4d42-44c8-b77b-fac094f19356@oracle.com> Message-ID: <1aeb6d9e-7cd8-47b9-957a-f9ed06d44c19@oracle.com> Filed a PR for (1): https://git.openjdk.org/jdk/pull/19836 Maurizio On 21/06/2024 16:12, Archie Cobbs wrote: > Sounds good - thanks. > > On Fri, Jun 21, 2024 at 9:26?AM Maurizio Cimadamore > wrote: > > Actually, on a second thought, let's just reuse the bugs we have > for these issues - e.g. > > 1. https://bugs.openjdk.org/browse/JDK-8334037 > 2. https://bugs.openjdk.org/browse/JDK-8334121 > 3. https://bugs.openjdk.org/browse/JDK-8334248 > > If that's ok, I'd suggest to withdraw the PR for (3), and assign > that issue to me. > > Cheers > Maurizio > > On 20/06/2024 22:50, Archie Cobbs wrote: >> On Thu, Jun 20, 2024 at 4:03?PM Maurizio Cimadamore >> wrote: >> >> So, to recap, we need 4 issues: >> >> 1. move LambdaToMethod after Lower >> 2. fix Lower so that it doesn't include inaccessible >> enclosing instances in the this$xyz chain >> 3. fix the frontend, so that type-checking of instance >> creation expression behaves as expected >> 4. do a final pass on the error messages >> >> 0.5.?Wrong bug number in regression test for JDK-8334252 (just >> created as JDK-8334679 ) >> >> Thanks, >> -Archie >> >> -- >> Archie L. Cobbs > > > > -- > Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Jun 25 07:04:49 2024 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 25 Jun 2024 09:04:49 +0200 (CEST) Subject: yield can not be followed by an underscore ? Message-ID: <1709036168.58626999.1719299089124.JavaMail.zimbra@univ-eiffel.fr> Hello, javac is not able to parse "yield _ -> ...". Here is a reproducer. public class SwitchYieldAndUnderscore { public static void main(String[] args) { Consumer value = switch (args[0]) { case "foo" -> { yield _ -> {}; } default -> throw new AssertionError(); }; } } regards, R?mi From rotanolexandr842 at gmail.com Tue Jun 25 10:11:39 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Tue, 25 Jun 2024 13:11:39 +0300 Subject: yield can not be followed by an underscore ? In-Reply-To: <1709036168.58626999.1719299089124.JavaMail.zimbra@univ-eiffel.fr> References: <1709036168.58626999.1719299089124.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hello Remi. Have you created issue related to this bug? It seems like it has been around for quite a while based on what I saw in block parsing code of javac On Tue, Jun 25, 2024, 10:05 Remi Forax wrote: > Hello, > javac is not able to parse "yield _ -> ...". > > Here is a reproducer. > > public class SwitchYieldAndUnderscore { > public static void main(String[] args) { > Consumer value = switch (args[0]) { > case "foo" -> { > yield _ -> {}; > } > default -> throw new AssertionError(); > }; > } > } > > regards, > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From rotanolexandr842 at gmail.com Tue Jun 25 10:52:02 2024 From: rotanolexandr842 at gmail.com (Olexandr Rotan) Date: Tue, 25 Jun 2024 13:52:02 +0300 Subject: yield can not be followed by an underscore ? In-Reply-To: References: <1709036168.58626999.1719299089124.JavaMail.zimbra@univ-eiffel.fr> Message-ID: I`ve filed the issue and opened corresponding pull request with fix: https://github.com/openjdk/jdk/pull/19878. Hope issue gets published soon On Tue, Jun 25, 2024 at 1:11?PM Olexandr Rotan wrote: > Hello Remi. Have you created issue related to this bug? It seems like it > has been around for quite a while based on what I saw in block parsing code > of javac > > On Tue, Jun 25, 2024, 10:05 Remi Forax wrote: > >> Hello, >> javac is not able to parse "yield _ -> ...". >> >> Here is a reproducer. >> >> public class SwitchYieldAndUnderscore { >> public static void main(String[] args) { >> Consumer value = switch (args[0]) { >> case "foo" -> { >> yield _ -> {}; >> } >> default -> throw new AssertionError(); >> }; >> } >> } >> >> regards, >> R?mi >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: