From cay.horstmann at gmail.com Sat Oct 1 07:23:09 2022 From: cay.horstmann at gmail.com (Cay Horstmann) Date: Sat, 1 Oct 2022 08:23:09 +0100 Subject: Paving the on-ramp In-Reply-To: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> Message-ID: <65c2ce0d-295a-a4a4-e2cb-568419ba24c5@gmail.com> I would like to point out that in Java-based CS1 courses in US universities, there are typically two kinds of on-ramps, called "objects early" and "objects late". Some instructors care greatly about one approach or the other, and textbook authors (such as myself) produce two versions of their book to support the instructor preference. The objects late camp would love it if there was no visible class, main and all other methods/fields were static without being so declared, and System.out.println was called println. The objects early camp would prefer a visible class, with an instance method main (invoked on an instance constructed with the no-arg constructor). System.out.println could be replaced with System.println. But a raw println would be confusing--one must then explain that there is a magic method that is different from the instance methods that they are learning to write. I imagine that both camps would hope that String[] args can be optional for main. Objects late: int count; void doWork() { count++; println("Did work " + count); } void main() { doWork(); doWork(); } Objects early: class Worker { int count; void doWork() { count++; System.println("Did work " + count); } void main() { doWork(); doWork(); } } Cheers, Cay Il 28/09/2022 18:57, Brian Goetz ha scritto: > At various points, we've explored the question of which program elements are most and least helpful for students first learning Java.? After considering a number of alternatives over the years, I have a simple proposal for smoothing the "on ramp" to Java programming, while not creating new things to unlearn. > > Markdown source is below, HTML will appear soon at: > > https://openjdk.org/projects/amber/design-notes/on-ramp > > -- Cay S. Horstmann | http://horstmann.com | mailto:cay at horstmann.com From gavin.bierman at oracle.com Mon Oct 3 12:48:44 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Oct 2022 12:48:44 +0000 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns Message-ID: Dear all, The draft JEPs for the next preview of the Pattern Matching for switch and Record Patterns features are now available at: Pattern matching for switch: https://bugs.openjdk.org/browse/JDK-8294285 Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 Comments welcomed! Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Oct 3 13:40:44 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 3 Oct 2022 15:40:44 +0200 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: References: Message-ID: Hello! > Remove support for named record patterns. This surprises me. Probably there was a discussion about the rationale behind this change? Could you please point me? Thanks. With best regards, Tagir Valeev On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman wrote: > > Dear all, > > The draft JEPs for the next preview of the Pattern Matching for switch and Record Patterns features are now available at: > > Pattern matching for switch: https://bugs.openjdk.org/browse/JDK-8294285 > Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 > > Comments welcomed! > Gavin From gavin.bierman at oracle.com Mon Oct 3 15:29:40 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Oct 2022 15:29:40 +0000 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: References: Message-ID: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> Hi Tagir, So it was a number of issues actually. First, there is a nasty ambiguity problem. Consider: record R(){} switch(e) { case R() when when (true) -> ... ... } The label could be parsed as either: case (R() when) when (true) -> or case (R()) when (when(true)) -> (where `when` is a static boolean method). There is another issue which is this variable declaration is the only one in the language that can?t be annotated or marked as `final` which feels like a design smell. None of the obvious solutions to this looked good. In most other languages with pattern matching they keep these two things - a destructing pattern and a naming pattern - separate. In both Haskell and Scala, they write `x at p` to ?name? a pattern p as x. So that seems like a possibility. But for now, we noted that in most cases you can rewrite pretty simply, e.g. case Point(var x, var y) when p.isVisible() -> can be rewritten: case Point p when p.isVisible() && p instanceof Point(var x, var y): ? or if it was in an instanceof: if (x instanceof Point p && p.isVisible() && p instanceof Point(var x, var y)) { ? } Neither of these versions read too badly. Thoughts? Gavin On 3 Oct 2022, at 14:40, Tagir Valeev > wrote: Hello! Remove support for named record patterns. This surprises me. Probably there was a discussion about the rationale behind this change? Could you please point me? Thanks. With best regards, Tagir Valeev On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman > wrote: Dear all, The draft JEPs for the next preview of the Pattern Matching for switch and Record Patterns features are now available at: Pattern matching for switch: https://bugs.openjdk.org/browse/JDK-8294285 Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 Comments welcomed! Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Mon Oct 3 16:05:01 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Mon, 3 Oct 2022 16:05:01 +0000 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> Message-ID: <80C04B50-8BE9-4FCC-B51E-738FDC39BD46@oracle.com> case Point(var x, var y) when p.isVisible() -> Of course I meant: case Point(var x, var y) p when p.isVisible() -> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 3 16:25:48 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 3 Oct 2022 18:25:48 +0200 (CEST) Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> Message-ID: <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> > From: "Gavin Bierman" > To: "Tagir Valeev" > Cc: "amber-dev" , "amber-spec-experts" > > Sent: Monday, October 3, 2022 5:29:40 PM > Subject: Re: Draft JEPs: Pattern Matching for switch and Record Patterns > Hi Tagir, The main objection to remove the name of the record pattern is that it does not follow the principle of the data oriented programming. The idea is that the data is more important than the code, or said differently, if the data change by example a component is added to a record, the compiler should flag all the code that uses that record and ask the user to modify the code. So a case with a record pattern is better than just a type pattern, because unlike a type pattern, a record pattern validates the shape of a record case Point p // does not validate the shape of Point case Point(int x, int y) p // validates that a Point has two components x and y. When using virtual polymorphism, an operation is defined as an abstract method, so if the record shape changes, people will scan the rest of the record and change the implementation of the methods according to the new components. If the operation uses pattern matching, the record and the operation are not declared at the same place, so the compiler has to help users to find all the locations in the code that should be updated. > So it was a number of issues actually. First, there is a nasty ambiguity > problem. Consider: > record R(){} > switch(e) { > case R() when when (true) -> ... > ... > } > The label could be parsed as either: > case (R() when) when (true) -> > or > case (R()) when (when(true)) -> > (where ` when ` is a static boolean method). It's a usual issue with local keywords, we had the same kind of issue with the local keywords inside modules (transitive as a keyword or has a package name). A solution on top of my head is to make "when" a keyword for the whole case (everything in between "case" and "->"), so having to consecutive "when" is not syntactically valid. It's not the only option, and i don't think it's a showstopper. > There is another issue which is this variable declaration is the only one in the > language that can?t be annotated or marked as `final` which feels like a design > smell. None of the obvious solutions to this looked good. For me, a group pattern is not really different than a type pattern for this concern, the current syntax is with a type pattern is case final Point p -> so the syntax for a record pattern is case final Point(int x,int y) p -> It awkward and super verbose but it's a straight consequence of not having the binding always final. > In most other languages with pattern matching they keep these two things - a > destructing pattern and a naming pattern - separate. In both Haskell and Scala, > they write `x at p` to ?name? a pattern p as x. So that seems like a possibility. > But for now, we noted that in most cases you can rewrite pretty simply, e.g. > case Point(var x, var y) when p.isVisible() -> > can be rewritten: > case Point p > when p.isVisible() && p instanceof Point(var x, var y): ? > or if it was in an instanceof: > if (x instanceof Point p && p.isVisible() && p instanceof Point(var x, var y)) { > ? } > Neither of these versions read too badly. I disagree, a case ... does not exist in the vacuum but works and is read with the other cases. Here, following "case Point p when ... ", you will have a proper record pattern of Point to be exhaustive and the lack of common prefix between the two patterns makes the code hard to read. Point p = ... switch(p) { case Point p when p.isVisible() && p instanceof Point (var x, var y) -> ... case Point(int x, int y) -> ... } compared to Point p = ... switch(p) { case Point(int x, int y) p when p.isVisible() -> ... case Point(int x, int y) p -> ... } Here, it's clear that the first line is a peculiar case of the second line. > Thoughts? > Gavin R?mi >> On 3 Oct 2022, at 14:40, Tagir Valeev < [ mailto:amaembo at gmail.com | >> amaembo at gmail.com ] > wrote: >> Hello! >>> Remove support for named record patterns. >> This surprises me. Probably there was a discussion about the rationale >> behind this change? Could you please point me? Thanks. >> With best regards, >> Tagir Valeev >> On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman < [ mailto:gavin.bierman at oracle.com >> | gavin.bierman at oracle.com ] > wrote: >>> Dear all, >>> The draft JEPs for the next preview of the Pattern Matching for switch and >>> Record Patterns features are now available at: >>> Pattern matching for switch: [ https://bugs.openjdk.org/browse/JDK-8294285 | >>> https://bugs.openjdk.org/browse/JDK-8294285 ] >>> Record Patterns: [ https://bugs.openjdk.org/browse/JDK-8294078 | >>> https://bugs.openjdk.org/browse/JDK-8294078 ] >>> Comments welcomed! >>> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Oct 3 22:16:21 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 3 Oct 2022 18:16:21 -0400 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> Message-ID: <277f5d2c-d36a-e791-48d8-cd35e10bc8f7@oracle.com> I was skeptical at first too, but the weight of the points Gavin raises makes me feel that this aspect was (a) a little rushed and (b) not critical, so backing it out now gives us a chance to think it through further, and bring it back later in this or another form. On 10/3/2022 12:25 PM, Remi Forax wrote: > > > ------------------------------------------------------------------------ > > *From: *"Gavin Bierman" > *To: *"Tagir Valeev" > *Cc: *"amber-dev" , "amber-spec-experts" > > *Sent: *Monday, October 3, 2022 5:29:40 PM > *Subject: *Re: Draft JEPs: Pattern Matching for switch and Record > Patterns > > Hi Tagir, > > > The main objection to remove the name of the record pattern is that it > does not follow the principle of the data oriented programming. > The idea is that the data is more important than the code, or said > differently, if the data change by example a component is added to a > record, the compiler should flag all the code that uses that record > and ask the user to modify the code. > > So a case with a record pattern is better than just a type pattern, > because unlike a type pattern, a record pattern validates the shape of > a record > ? case Point p?? // does not validate the shape of Point > ? case Point(int x, int y) p? // validates that a Point has two > components x and y. > > When using virtual polymorphism, an operation is defined as an > abstract method, so if the record shape changes, people will scan the > rest of the record and change the implementation of the methods > according to the new components. If the operation uses pattern > matching, the record and the operation are not declared at the same > place, so the compiler has to help users to find all the locations in > the code that should be updated. > > > So it was a number of issues actually. First, there is a nasty > ambiguity problem. Consider: > > record R(){} > switch(e) { > ? ? case R() when when (true) -> ... > ? ? ... > } > > The label could be parsed as either: > > ? ? case (R() when) when (true) -> > > or > > ? ? case (R()) when (when(true)) -> > > (where `when` is a static boolean method). > > > It's a usual issue with local keywords, we had the same kind of issue > with the local keywords inside modules (transitive as a keyword or has > a package name). > > A solution on top of my head is to make "when" a keyword for the whole > case (everything in between "case" and "->"), so having to consecutive > "when" is not syntactically valid. > It's not the only option, and i don't think it's a showstopper. > > > > There is another issue which is this variable declaration is the > only one in the language that can?t be annotated or marked as > `final` which feels like a design smell. None of the obvious > solutions to this looked good. > > > For me, a group pattern is not really different than a type pattern > for this concern, > the current syntax is with a type pattern is > ? case final Point p -> > so the syntax for a record pattern is > ? case final Point(int x,int y) p -> > > It awkward and super verbose but it's a straight consequence of not > having the binding always final. > > > In most other languages with pattern matching they keep these two > things - a destructing pattern and a naming pattern - separate. In > both Haskell and Scala, they write `x at p` to ?name? a pattern p as > x. So that seems like a possibility. But for now, we noted that in > most cases you can rewrite pretty simply, e.g. > > case?Point(var x, var y) when p.isVisible() -> > > can be rewritten: > > ? ? case Point p > ? ? ? ? when p.isVisible() && p instanceof Point(var x, var y): ? > > or if it was in an instanceof: > > ? ? if (x instanceof Point p && p.isVisible() && p instanceof > Point(var x, var y)) {???} > > Neither of these versions read too badly. > > > I disagree, a case ... does not exist in the vacuum but works and is > read with the other cases. > Here, following "case Point p when ... ", you will have a proper > record pattern of Point to be exhaustive and the lack of common prefix > between the two patterns makes the code hard to read. > > ? Point p = ... > ? switch(p) { > ?? case Point p > ???? when p.isVisible() && p instanceof Point (var x, var y) -> ... > ?? case Point(int x, int y) -> ... > ? } > > compared to > ? Point p = ... > ? switch(p) { > ?? case Point(int x, int y) p when p.isVisible() -> ... > ?? case Point(int x, int y) p -> ... > ? } > > Here, it's clear that the first line is a peculiar case of the second > line. > > > Thoughts? > > Gavin > > > R?mi > > > > > On 3 Oct 2022, at 14:40, Tagir Valeev wrote: > > Hello! > > Remove support for named record patterns. > > > This surprises me. Probably there was a discussion about the > rationale > behind this change? Could you please point me? Thanks. > > With best regards, > Tagir Valeev > > On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman > wrote: > > > Dear all, > > The draft JEPs for the next preview of the Pattern > Matching for switch and Record Patterns features are now > available at: > > Pattern matching for switch: > https://bugs.openjdk.org/browse/JDK-8294285 > Record Patterns: https://bugs.openjdk.org/browse/JDK-8294078 > > Comments welcomed! > Gavin > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Oct 6 16:45:09 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 6 Oct 2022 12:45:09 -0400 Subject: Fwd: Java on-ramp - Class declaration for Entrypoints In-Reply-To: References: Message-ID: <51b4332b-728f-a859-eef6-9b36148f87d9@oracle.com> This was received today on the -comments list; Stephen has an alternate proposal for the on-ramp, making "entry points" (programs) a first class concept. This proposal is actually three proposals in one, which are actually pretty separable: ?- A syntax-heavy proposal for "entrypoints"; ?- An alternate way to address console IO, with new classes for SystemOut rather than new static functionality; ?- A further evolution on the "instance main" notion to use an interface to identify the "instance main" entry point, rather than extending the existing structural recognition. I will mostly speak to the first of these.? The second is completely orthogonal (it is not essential to this proposal, and could be taken separately without dragging in anything else). This is largely an API design question, and can be discussed when we get to how to best expose new functionality for console IO.? The third has been mentioned on the EG list already, that "instance main" is an opportunity to switch to something more nominal.? I'll come back to this later in this mail. To the main part of this proposal, I explored several similar points in my internal exploration (including also a method-centric version of the same basic split), prior to publishing my "On Ramp" document.? (Spoiler: ultimately I didn't feel these approaches carried their weight.) In its favor, the proposal is principled: that of all the concepts that we ask users to confront in Hello World that have syntactic representations, perhaps the most important one for the situation at hand -- a *program* -- doesn't even have a syntactic representation in the program.? Java takes the worse-is-better approach here of saying "a program is just a class with a main method."? This is a pragmatic tradeoff, but it means that in the big list of concepts that users are confronted with, we are not able to whittle it down to zero -- users still have to confront methods (and their affordances such as return types and parameter lists.)? There's still a conceptual gap here, because these affordances largely support invoking methods (which the user has not yet learned about), but somehow the "main" method gets called magically by the launcher.? I spent some time exploring whether we could remove these last things from the list, but concluded that the juice was not worth the squeeze. Stephen approaches this by a classic "split" move -- split methods into two kinds: "main" methods and "regular" ones, giving special syntax to the new thing, and then adopting a similar "unnamed classes" approach for classes with a main method.? This splitting is motivated by several forces: that a "program" is an important concept to represent, and that the current treatment of "main" in the language is frustratingly structural in a language where nearly everything else is nominal. I sympathize with the concern that "main-ness" is too magic; when I learned Java 25+ years ago, among my first reactions was "but where's the program".? That a Java program is merely a soup of classes loosely contained by tooling switches or environment variables, and a program could have as many "main" entry points as it has classes, does take some getting used to (though less now than it did then, when linkers roamed the earth.)? And I sympathize with the desire to fix the mistakes of the past.? But I think that the "entrypoint" proposal strikes the wrong balance.? It has way too much new surface syntax, while at the same time, leaving the existing static main protocol flapping in the breeze.? Given where we are, I don't see it as carrying its weight.? Stephen thinks it is "better bang for buck", but I disagree, not necessarily because of the bang part, but the buck part -- it is dramatically more expensive in all the dimensions (spec, syntax, user perception, implementation cost, etc). The two sub-proposals are more compelling, and both are sure to play into the discussions to come in any case.? New classes vs new static entry points is a totally valid API design discussion to have.? Similarly, with the addition of "instance main", it is a totally sensible question to ask whether we want to extend the existing structural recognition of main methods to the new thing, or break from that and use interfaces.? Both approaches have pros and cons, and we can discuss those. -------- Forwarded Message -------- Subject: Java on-ramp - Class declaration for Entrypoints Date: Thu, 6 Oct 2022 12:27:06 +0100 From: Stephen Colebourne To: amber-spec-comments at openjdk.java.net Hi all, I wrote up my response to the on-ramp discussion as a blog post having given it a few days thought: https://blog.joda.org/2022/10/fully-defined-entrypoints.html I think what I've come up with is a lot more powerful, more useful to experienced developers, and more consistent during the learning process. It is, of course, a bigger change. This email to amber-spec-comments reads the idea into the legal-world of OpenJDK.. thanks Stephen

Entrypoints

When a Java program starts some kind of class file needs to be run. It could be a normal class, but that isn't ideal as we don't really want static/instance variables, subclasses, parent interfaces, access control etc. One suggestion was for it to be a normal interface, but that isn't ideal as we don't want to mark the methods as default or allow abstract methods.

I'd like to propose that what Java needs is a new kind of class declaration for entrypoints.

I don't think this is overly radical. We already have two alternate class declarations - record and enum. They have alternate syntax that compiles to a class file without being explictly a class in source code. What we need here is a new kind - entrypoint - that compiles to a class file but has different syntax rules, just like record and enum do.

I believe this is a fundamentally better approach than the minor tweaks in the official proposal, because it will be useful to developers of all skill levels, including framework authors. ie. it has a much better "bang for buck".

The simplest entrypoint would be:

// MyMain.java
entrypoint {
SystemOut.println("Hello World");
}

In the source code we have various things:

  • Inferred class name from file name, the class file is MyMain$entrypoint
  • Top-level code, no need to discuss methods initially
  • No access to paraneters
  • New classes SystemOut, SystemIn and SystemErr
  • No constructor, as a new kind of class declaration it doesn't need it

The classes like SystemOut may seem like a small change, but it would have been much simpler for me from 25 years ago to understand. I don't favour more static imports for them (either here or more generally), as I think SystemOut.println("Hello World") is simple enough. More static imports would be too magical in my opinion.

The next steps when learning Java are for the instructor to expand the entrypoint.

  • Add a named method (always private, any name although main() would be common)
  • Add parameters to the method (maybe String[], maybe String...)
  • Add return type to the method (void is default return type)
  • Group code into a block
  • Add additional methods (always private)

Here are some valid examples. Note that instructors can choose the order to explain each feature:

entrypoint {
SystemOut.println("Hello World");
}
entrypoint main() {
SystemOut.println("Hello World");
}
entrypoint main(String[] args) {
SystemOut.println("Hello World");
}
entrypoint {
main() {
SystemOut.println("Hello World");
}
}
entrypoint {
void main(String[] args) {
SystemOut.println("Hello World");
}
}
entrypoint {
main(String[] args) {
output("Hello World");
}
output(String text) {
SystemOut.println(text);
}
}

Note that there are never any static methods, static variables, instance variables or access control. If you need any of that you need a class. Thus we have proper separation of concerns for the entrypoint of systems, which would be Best Practice even for experienced developers.

Progressing to classes

During initial learning, the entrypoint class declaration and normal class declaration would be kept in separate files:

// MyMain.java
entrypoint {
SystemOut.println(new Person().name());
}
// Person.java
public class Person {
String name() {
return "Bob";
}
}

However, at some point the instructor would embed an entrypoint (of any valid syntax) in a normal class.

public class Person {
entrypoint {
SystemOut.println(new Person().name());
}
String name() {
return "Bob";
}
}

We discover that an entrypoint is normally wrapped in a class which then offers the ability to add static/instance variables and access control.

Note that since all methods on the entrypoint are private and the entrypoint is anonymous, there is no way for the rest of the code to invoke it without hackery. Note also that the entrypoint does not get any special favours like an instance of the outer class, thus there is no issue with no-arg constructors - if you want an instance you have to use new (the alternative is unhelpful magic that harms learnability IMO).

Finally, we see that our old-style static main method is revealed to be just a normal entrypoint:

public class Person {
entrypoint public static void main(String[] args) {
SystemOut.println(new Person().name());
}
String name() {
return "Bob";
}
}

ie. when a method is declared as public static void main(String[]) the keyword entrypoint is implicitly added.

What experienced developers gain from this is a clearer way to express what the entrypoint actually is, and more power in expressing whether they want the command line arguments or not.

Full-featured entrypoints

Everything above is what most Java developers would need to know. But an entrypoint would actually be a whole lot more powerful.

The basic entrypoint would compile to a class something like this:

// MyMain.java
entrypoint startHere(String[] args) {
SystemOut.println("Hello World");
}
// MyMain$entrypoint.class
public final MyMain$entrypoint implements java.lang.Entrypoint {
@Override
public void main(Runtime runtime) {
runtime.execute(() -> startHere(runtime.args()));
}
private void startHere(String[] args) {
SystemOut.println("Hello World");
}
}

Note that it is final and methods are private.

The Entrypoint interface would be:

public interface java.lang.Entrypoint {
/**
* Invoked by the JVM to launch the program.
* When the method completes, the JDK terminates.
*/
public abstract void main(Runtime runtime);
}

The Runtime.execute method would be something like:

public void execute(ThrowableRunnable runnable) {
try {
runnable.run();
System.exit(0);
} catch (Throwable ex) {
ex.printStackTrace();
System.exit(1);
}
}

The JVM would do the following:

  • Load the class file specified on the command line
  • If it implements java.lang.Entrypoint call the no-args constructor and invoke it
  • Else look for a legacy public static void main(String[]), and invoke that

Note that java.lang.Entrypoint is a normal interface that can be implemented by anyone and do anything!

This last point is critical to enhancing the bang-for-buck. I was intriguied by things like Azul CRaC which wants to own the whole lifecycle of the JVM run. Wouldn't that be more powerful if they could control the whole lifecycle through Entrypoint. Another possibile use is to reset the state when an application has finished, allowing the same JVM to be reused - a bit like Function-as-a-Service providers or build system daemons do. (I suspect it may be possible to enhance the entrypoint concept to control the shutdown hooks and to catch things like System.exit but that is beyond the scope of this blog.) For example, here is a theoretical application framework entrypoint:

// FrameworkApplication.java - an Open Source library
public interface FrameworkApplication extends Entrypoint {
public default main(Runtime runtime) {
// do framework things
start();
// do framework things
}
public abstract start();
}

Applications just implement this interface, and they can run it by specifying their own class name on the command line, yet it is a full-featured framework application!

-------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Oct 12 23:56:44 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 12 Oct 2022 19:56:44 -0400 Subject: Paving the on-ramp In-Reply-To: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> Message-ID: I've gotten some good feedback from this writeup, much of it illuminating how different people (myself included) make different assumptions about how certain things work / what things are for. #### Role of main A main() method can be viewed from two perspectives: it is an ordinary method with modifiers, parameters, etc, but it is also an externally significant _entry point_ into a program.? (We've seen a similar double-duty with method parameters in the context of the underscore discussion: a method parameter is both a "local" that serves the implementation of the method, but its also part of the specification of the method (e.g., the name and type bleed into the Javadoc.)) Java made the choice in 1995 that an entry point is "just" a suitably named / structured / accessible method.? This was somewhat "un-Java-ish", as identification of main-ness is a structural property, and Java (certainly back then) mostly avoided such structural techniques where it could.? I think its fair to question whether this decision is still serving us (Stephen's proposal does exactly that), but I think this was a pragmatic choice and I'm inclined to continue to run with it, rather than look at it as something "to be fixed". So the proposal broadens the structure parameters around "what is a main", but ultimately leaves the "some methods are special" decision in place.? (Aside: if we'd had annotations in Java 1.0, we might well have used an @EntryPoint capture the metadata of "this method is externally launchable", and let the launcher reflect over the methods of a class to find a suitable entry point.? But we didn't, and trying to rewrite history in this way is likely a losing game.) #### Role of launcher This raises a secondary question about the role of the launcher.? My mental model was one of "the launcher is just some out-of-sight code that invokes the main method."? That `main` (currently) should be public plays into this, because if this out-of-sight code is going to call this method, surely accessibility will have a chance to say "can this code call this method".? I was surprised, though, to find out that others have a different view of this.? Kevin asked whether it was a "bug" in the model that, in order for the launcher to be able to call a `main` method, then so must other code.? This is a form of coupling that I had assumed is pure good (because it means there's a unified model of invocation), but is reasonable to call it out as a coupling. In my view, treating the launcher as "just another caller" simplifies the story, because we don't have to have a separate story for invoking entry points.? If we're doubling down on "main is just a method with some special metadata that says 'start here'", then doubling down on accessibility here as well makes sense to me also.? Hence my suggestion to frame the launcher as "ordinary code in the unnamed package".? But as natural as this seems to me, I recognize that this may seem weird to others. #### Role of unnamed packages Another place where there were differing viewpoints is on the role of unnamed packages.? Some felt that using the unnamed package at all should be reserved for the most transient of "run it once and delete it" programs; others felt that it had a role beyond mere experiments. In part because of the difficulties described in the On Ramp document, Java has not found extensive use in scripts, but we are hoping that this will become more common over time.? (The "Launch Single File Java Programs" JEP was a step towards being more script-friendly.)? I would think that the unnamed package is the right place for scripts as well, since users will want to identify the program by its path in the file system, not by setting up a class path and referring to a relative position within that. Overall, I think the result of this project will be that *more* code lives in the unnamed package -- and that's OK, because people will write more simple, self-contained programs in Java, and the unnamed package is an OK place for these things to live. #### Instance main and interfaces Several folks have pointed out that perhaps an instance main should be specified by an interface (say, Program), and that unnamed classes implicitly implement Program.? This has some attraction, since this is generally how we prevent names from being magic. There are some downsides, though.? We already have two ways to spell "main"; we might want more in the future, and having to spell them all out in an interface (even with defaults) could impede evolution.? More importantly, this is using a different mechanism that we use for the main we currently have, which means there are two different ways to identify the two kinds of main.? This is starting to veer into the sort of "fix mistakes of the past" siren song which leaves you with two different ways of doing the same thing, which appears to new users as more complexity, not less. #### Moral hazards A significant proportion of the negative feedback to the proposal amounted to some sort of "moral hazard".? The biggest component here was that shortening `System.out.println()` to `println()` would encourage people to use console IO more often, in places where it was not appropriate, or that it would be an impediment to testing.? I didn't find this argument very compelling, though; I don't think the number of characters required to access console IO is the determining factor here in choosing an IO strategy (or even being aware that there is such a thing as an IO strategy.) #### Console IO There were a variety of opinions on the direction we should go with simplifying console IO.? While nearly everyone agrees that `System.out.println` is a mouthful, there were a number of alternate approaches suggested, such as: ?- Expose new classes (e.g., SystemOut) with static methods (SystemOut.println) instead of new static methods; ?- Expose a new API for console IO, but don't auto-import anything; ?- Auto-import console IO methods only in unnamed classes; ?- others. #### Auto imports After some thought, I realize that the auto-import feature is trying to serve two separate goals, and maybe is getting distorted because of it.? The String Templates JEP defines some canned processors (STR and FMT), which are effectively part of the language.? At first blush (which is when people are often most vocal about peripheral features), people already don't like having to type STR-dot (sorry, not changing our mind about that), and having to import STR feels like adding insult to injury.? STR is effectively part of the language, so we don't want to place hoops in the way of using it.? But that is a slightly different use case than rescuing println, for which there are multiple possible paths.? Maybe its best to separate these. The On Ramp document outlined some (admittedly fuzzy) goals which may be helpful for evaluating some of these thoughts.? We are not trying to create a "beginner's dialect of Java", nor am I particularly interested in using this effort to turn back the clock and redesign features that some people wish had been designed otherwise.? The goal here is, to the extent possible, to make elements of program declaration recede into the background until they are actually adding value.? Access control, classes, statics, etc, should come into the picture when they are needed, and not before.? I'm sure that there are other aspects of the language that people would like to have recede into the background too, and will attempt to attach to this project, but I'm interested in focusing primarily on those that are impediments to learning the language.? If we do our job right, these will have benefits well beyond the first few days of using Java. My thoughts: ?- Java made the choice that "entry points" are specially structured methods, and can be called both programmatically and via the launcher.? I see no reason to tinker with this concept, or to materially change how entry points are declared -- just to widen the range of `main` methods that are considered entry points. ?- While the JLS doesn't really say what the launcher is (just that there is something that launches programs), positioning the launcher as some code in the unnamed package seems helpful, because familiar concepts of access control apply both to programmatic and command line invocation. ?- While I get that some people find the unnamed package to be a "smell", I think it is actually fine for a range of programs, and I expect that range to broaden over time as people start using Java for scripts. ?- I am sympathetic to the argument that people don't want "println" and friends to effectively become global; this is separate from simplifying the locution.? Where there may be an attractive middle ground is: ??? - Take the new methods for console IO and put them somewhere in java.lang, say ConsoleIO; ??? - Auto-import ConsoleIO only in unnamed classes. While this creates a new coupling, it may align well, in that the programs who most want access to console IO are those that are most likely to be able to be unnamed classes -- and the rest can import all the console IO with a single `import static ConsoleIO.*`.? Refactoring to a named class would then involve adding both the class wrapper and this import. ?- I am open to other approaches for improving the console IO story.? Maybe this is Console.println(), which avoids the need for auto-static-import at all at the cost of some more words (but at least doesn't require understanding static fields, and has the advantage that all the console stuff can be in one place), or maybe something else.? (A big part of the problem with the status quo now is that not only is System.out.println undiscoverable, but having found that, you still have no idea where "readln" is.) ?- While I get why people are attracted to putting instance main in an interface, I don't think I'd like the result.? As mentioned above, this implicitly places an impediment in future expansion of the launcher protocol, but more importantly, means we have two mechanisms for finding main -- an "old" and "new" one.? I try hard to avoid creating this situation, because the benefit is rarely worth the increased cognitive load. On 9/28/2022 1:57 PM, Brian Goetz wrote: > At various points, we've explored the question of which program > elements are most and least helpful for students first learning Java.? > After considering a number of alternatives over the years, I have a > simple proposal for smoothing the "on ramp" to Java programming, while > not creating new things to unlearn. > > Markdown source is below, HTML will appear soon at: > > https://openjdk.org/projects/amber/design-notes/on-ramp > > > # Paving the on-ramp > > Java is one of the most widely taught programming languages in the > world.? Tens > of thousands of educators find that the imperative core of the > language combined > with a straightforward standard library is a foundation that students can > comfortably learn on.? Choosing Java gives educators many degrees of > freedom: > they can situate students in `jshell` or Notepad or a full-fledged > IDE; they can > teach imperative, object-oriented, functional, or hybrid programming > styles; and > they can easily find libraries to interact with external data and > services. > > No language is perfect, and one of the most common complaints about > Java is that > it is "too verbose" or has "too much ceremony."? And unfortunately, > Java imposes > its heaviest ceremony on those first learning the language, who need and > appreciate it the least.? The declaration of a class and the > incantation of > `public static void main` is pure mystery to a beginning programmer.? > While > these incantations have principled origins and serve a useful > organizing purpose > in larger programs, they have the effect of placing obstacles in the > path of > _becoming_ Java programmers. Educators constantly remind us of the > litany of > complexity that students have to confront on Day 1 of class -- when > they really > just want to write their first program. > > As an amusing demonstration of this, in her JavaOne keynote appearance > in 2019, > [Aimee Lucido](https://www.youtube.com/watch?v=BkPPFiXUwYk) talked > about when > she learned to program in Java, and how her teacher performed a rap song > to help students memorize `"public static void main"`.? Our hats are > off to > creative educators everywhere for this kind of dedication, but teachers > shouldn't have to do this. > > Of course, advanced programmers complain about ceremony too. We will > never be > able to satisfy programmers' insatiable appetite for typing fewer > keystrokes, > and we shouldn't try, because the goal of programming is to write > programs that > are easy to read and are clearly correct, not programs that were easy > to type. > But we can try to better align the ceremony commensurate with the value it > brings to a program -- and let simple programs be expressed more simply. > > ## Concept overload > > The classic "Hello World" program looks like this in Java: > > ``` > public class HelloWorld { > ??? public static void main(String[] args) { > ??????? System.out.println("Hello World"); > ??? } > } > ``` > > It may only be five lines, but those lines are packed with concepts > that are > challenging to absorb without already having some programming > experience and > familiarity with object orientation. Let's break down the concepts a > student > confronts when writing their first Java program: > > ? - **public** (on the class).? The `public` accessibility level is > relevant > ??? only when there is going to be cross-package access; in a simple > "Hello > ??? World" program, there is only one class, which lives in the > unnamed package. > ??? They haven't even written a one-line program yet; the notion of access > ??? control -- keeping parts of a program from accessing other parts > of it -- is > ??? still way in their future. > > ? - **class**.? Our student hasn't set out to write a _class_, or model a > ??? complex system with objects; they want to write a _program_.? In > Java, a > ??? program is just a `main` method in some class, but at this point > our student > ??? still has no idea what a class is or why they want one. > > ? - **Methods**.? Methods are of course a key concept in Java, but the > mechanics > ??? of methods -- parameters, return types, and invocation -- are still > ??? unfamiliar, and the `main` method is invoked magically from the `java` > ??? launcher rather than from explicit code. > > ? - **public** (again).? Like the class, the `main` method has to be > public, but > ??? again this is only relevant when programs are large enough to require > ??? packages to organize them. > > ? - **static**.? The `main` method has to be static, and at this > point, students > ??? have no context for understanding what a static method is or why > they want > ??? one.? Worse, the early exposure to `static` methods will turn out > to be a > ??? bad habit that must be later unlearned.? Worse still, the fact > that the > ??? `main` method is `static` creates a seam between `main` and other > methods; > ??? either they must become `static` too, or the `main` method must > trampoline > ??? to some sort of "instance main" (more ceremony!)? And if we get > this wrong, > ??? we get the dreaded and mystifying `"cannot be referenced from a static > ??? context"` error. > > ? - **main**.? The name `main` has special meaning in a Java program, > indicating > ??? the starting point of a program, but this specialness hides behind > being an > ??? ordinary method name.? This may contribute to the sense of "so > many magic > ??? incantations." > > ? - **String[]**.? The parameter to `main` is an array of strings, > which are the > ??? arguments that the `java` launcher collected from the command > line.? But our > ??? first program -- likely our first dozen -- will not use command-line > ??? parameters. Requiring the `String[]` parameter is, at this point, > a mistake > ??? waiting to happen, and it will be a long time until this parameter > makes > ??? sense.? Worse, educators may be tempted to explain arrays at this > point, > ??? which further increases the time-to-first-program. > > ? - **System.out.println**.? If you look closely at this incantation, each > ??? element in the chain is a different thing -- `System` is a class > (what's a > ??? class again?), `out` is a static field (what's a field?), and > `println` is > ??? an instance method.? The only part the student cares about right > now is > ??? `println`; the rest of it is an incantation that they do not yet > understand > ??? in order to get at the behavior they want. > > That's a lot to explain to a student on the first day of class.? > There's a good > chance that by now, class is over and we haven't written any programs > yet, or > the teacher has said "don't worry what this means, you'll understand > it later" > six or eight times.? Not only is this a lot of _syntactic_ things to > absorb, but > each of those things appeals to a different concept (class, method, > package, > return value, parameter, array, static, public, etc) that the student > doesn't > have a framework for understanding yet.? Each of these will have an > important > role to play in larger programs, but so far, they only contribute to "wow, > programming is complicated." > > It won't be practical (or even desirable) to get _all_ of these > concepts out of > the student's face on day 1, but we can do a lot -- and focus on the > ones that > do the most to help beginners understand how programs are constructed. > > ## Goal: a smooth on-ramp > > As much as programmers like to rant about ceremony, the real goal here > is not > mere ceremony reduction, but providing a graceful _on ramp_ to Java > programming. > This on-ramp should be helpful to beginning programmers by requiring > only those > concepts that a simple program needs. > > Not only should an on-ramp have a gradual slope and offer enough > acceleration > distance to get onto the highway at the right speed, but its direction > must > align with that of the highway.? When a programmer is ready to learn > about more > advanced concepts, they should not have to discard what they've > already learned, > but instead easily see how the simple programs they've already written > generalize to more complicated ones, and both the syntatic and conceptual > transformation from "simple" to "full blown" program should be > straightforward > and unintrusive.? It is a definite non-goal to create a "simplified > dialect of > Java for students". > > We identify three simplifications that should aid both educators and > students in > navigating the on-ramp to Java, as well as being generally useful to > simple > programs beyond the classroom as well: > > ?- A more tolerant launch protocol > ?- Unnamed classes > ?- Predefined static imports for the most critical methods and fields > > ## A more tolerant launch protocol > > The Java Language Specification has relatively little to say about how > Java > "programs" get launched, other than saying that there is some way to > indicate > which class is the initial class of a program (JLS 12.1.1) and that a > public > static method called `main` whose sole argument is of type `String[]` > and whose > return is `void` constitutes the entry point of the indicated class. > > We can eliminate much of the concept overload simply by relaxing the > interactions between a Java program and the `java` launcher: > > ?- Relax the requirement that the class, and `main` method, be > public.? Public > ?? accessibility is only relevant when access crosses packages; simple > programs > ?? live in the unnamed package, so cannot be accessed from any other > package > ?? anyway.? For a program whose main class is in the unnamed package, > we can > ?? drop the requirement that the class or its `main` method be public, > ?? effectively treating the `java` launcher as if it too resided in > the unnamed > ?? package. > > ?- Make the "args" parameter to `main` optional, by allowing the > `java` launcher to > ?? first look for a main method with the traditional `main(String[])` > ?? signature, and then (if not found) for a main method with no arguments. > > ?- Make the `static` modifier on `main` optional, by allowing the > `java` launcher to > ?? invoke an instance `main` method (of either signature) by > instantiating an > ?? instance using an accessible no-arg constructor and then invoking > the `main` > ?? method on it. > > This small set of changes to the launch protocol strikes out five of > the bullet > points in the above list of concepts: public (twice), static, method > parameters, > and `String[]`. > > At this point, our Hello World program is now: > > ``` > class HelloWorld { > ??? void main() { > ??????? System.out.println("Hello World"); > ??? } > } > ``` > > It's not any shorter by line count, but we've removed a lot of "horizontal > noise" along with a number of concepts.? Students and educators will > appreciate > it, but advanced programmers are unlikely to be in any hurry to make these > implicit elements explicit either. > > Additionally, the notion of an "instance main" has value well beyond > the first > day.? Because excessive use of `static` is considered a code smell, many > educators encourage the pattern of "all the static `main` method does is > instantiate an instance and call an instance `main` method" anyway.? > Formalizing > the "instance main" protocol reduces a layer of boilerplate in these > cases, and > defers the point at which we have to explain what instance creation is > -- and > what `static` is.? (Further, allowing the `main` method to be an > instance method > means that it could be inherited from a superclass, which is useful > for simple > frameworks such as test runners or service frameworks.) > > ## Unnamed classes > > In a simple program, the `class` declaration often doesn't help > either, because > other classes (if there are any) are not going to reference it by > name, and we > don't extend a superclass or implement any interfaces.? If we say an > "unnamed > class" consists of member declarations without a class header, then > our Hello > World program becomes: > > ``` > void main() { > ??? System.out.println("Hello World"); > } > ``` > > Such source files can still have fields, methods, and even nested > classes, so > that as a program evolves from a few statements to needing some > ancillary state > or helper methods, these can be factored out of the `main` method > while still > not yet requiring a full class declaration: > > ``` > String greeting() { return "Hello World"; } > > void main() { > ??? System.out.println(greeting()); > } > ``` > > This is where treating `main` as an instance method really shines; the > user has > just declared two methods, and they can freely call each other.? > Students need > not confront the confusing distinction between instance and static > methods yet; > indeed, if not forced to confront static members on day 1, it might be > a while > before they do have to learn this distinction.? The fact that there is a > receiver lurking in the background will come in handy later, but right > now is > not bothering anybody. > > [JEP 330](https://openjdk.org/jeps/330) allows single-file programs to be > launched directly without compilation; this streamlined launcher pairs > well with > unnamed classes. > > ## Predefined static imports > > The most important classes, such as `String` and `Integer`, live in the > `java.lang` package, which is automatically on-demand imported into all > compilation units; this is why we do not have to `import > java.lang.String` in > every class.? Static imports were not added until Java 5, but no > corresponding > facility for automatic on-demand import of common behavior was added > at that > time.? Most programs, however, will want to do console IO, and Java > forces us to > do this in a roundabout way -- through the static `System.out` and > `System.in` > fields.? Basic console input and output is a reasonable candidate for > auto-static import, as one or both are needed by most simple > programs.? While > these are currently instance methods accessed through static fields, > we can > easily create static methods for `println` and `readln` which are > suitable for > static import, and automatically import them.? At which point our > first program > is now down to: > > ``` > void main() { > ??? println("Hello World"); > } > ``` > > ## Putting this all together > > We've discussed several simplifications: > > ?- Update the launcher protocol to make public, static, and arguments > optional > ?? for main methods, and for main methods to be instance methods (when a > ?? no-argument constructor is available); > ?- Make the class wrapper for "main classes" optional (unnamed classes); > ?- Automatically static import methods like `println` > > which together whittle our long list of day-1 concepts down > considerably.? While > this is still not as minimal as the minimal Python or Ruby program -- > statements > must still live in a method -- the goal here is not to win at "code > golf".? The > goal is to ensure that concepts not needed by simple programs need not > appear in > those programs, while at the same time not encouraging habits that > have to be > unlearned as programs scale up. > > Each of these simplifications is individually small and unintrusive, > and each is > independent of the others.? And each embodies a simple transformation > that the > author can easily manually reverse when it makes sense to do so: elided > modifiers and `main` arguments can be added back, the class wrapper > can be added > back when the affordances of classes are needed (supertypes, > constructors), and > the full qualifier of static-import can be added back.? And these > reversals are > independent of one another; they can done in any combination or any order. > > This seems to meet the requirements of our on-ramp; we've eliminated > most of the > day-1 ceremony elements without introducing new concepts that need to be > unlearned. The remaining concepts -- a method is a container for > statements, and > a program is a Java source file with a `main` method -- are easily > understood in > relation to their fully specified counterparts. > > ## Alternatives > > Obviously, we've lived with the status quo for 25+ years, so we could > continue > to do so.? There were other alternatives explored as well; ultimately, > each of > these fell afoul of one of our goals. > > ### Can't we go further? > > Fans of "code golf" -- of which there are many -- are surely right now > trying to > figure out how to eliminate the last little bit, the `main` method, > and allow > statements to exist at the top-level of a program.? We deliberately > stopped > short of this because it offers little value beyond the first few > minutes, and > even that small value quickly becomes something that needs to be > unlearned. > > The fundamental problem behind allowing such "loose" statements is that > variables can be declared inside both classes (fields) and methods (local > variables), and they share the same syntactic production but not the same > semantics.? So it is unclear (to both compilers and humans) whether a > "loose" > variable would be a local or a field.? If we tried to adopt some sort > of simple > heuristic to collapse this ambiguity (e.g., whether it precedes or > follows the > first statement), that may satisfy the compiler, but now simple > refactorings > might subtly change the meaning of the program, and we'd be replacing the > explicit syntactic overhead of `void main()` with an invisible "line" > in the > program that subtly affects semantics, and a new subtle rule about the > meaning > of variable declarations that applies only to unnamed classes.? This > doesn't > help students, nor is this particularly helpful for all but the most > trivial > programs.? It quickly becomes a crutch to be discarded and unlearned, > which > falls afoul of our "on ramp" goals.? Of all the concepts on our list, > "methods" > and "a program is specified by a main method" seem the ones that are > most worth > asking students to learn early. > > ### Why not "just" use `jshell`? > > While JShell is a great interactive tool, leaning too heavily on it as > an onramp > would fall afoul of our goals.? A JShell session is not a program, but a > sequence of code snippets.? When we type declarations into `jshell`, > they are > viewed as implicitly static members of some unspecified class, with > accessibility is ignored completely, and statements execute in a > context where > all previous declarations are in scope.? This is convenient for > experimentation > -- the primary goal of `jshell` -- but not such a great mental model for > learning to write Java programs.? Transforming a batch of working > declarations > in `jshell` to a real Java program would not be sufficiently simple or > unintrusive, and would lead to a non-idiomatic style of code, because the > straightforward translation would have us redeclaring each method, > class, and > variable declaration as `static`.? Further, this is probably not the > direction > we want to go when we scale up from a handful of statements and > declarations to > a simple class -- we probably want to start using classes as classes, > not just > as containers for static members. JShell is a great tool for > exploration and > debugging, and we expect many educators will continue to incorporate > it into > their curriculum, but is not the on-ramp programming model we are > looking for. > > ### What about "always local"? > > One of the main tensions that `main` introduces is that most class > members are > not `static`, but the `main` method is -- and that forces programmers to > confront the seam between static and non-static members. JShell > answers this > with "make everything static". > > Another approach would be to "make everything local" -- treat a simple > program > as being the "unwrapped" body of an implicit main method.? We already > allow > variables and classes to be declared local to a method.? We could add > local > methods (a useful feature in its own right) and relax some of the > asymmetries > around nesting (again, an attractive cleanup), and then treat a mix of > declarations and statements without a class wrapper as the body of an > invisible > `main` method. This seems an attractive model as well -- at first. > > While the syntactic overhead of converting back to full-blown classes > -- wrap > the whole thing in a `main` method and a `class` declaration -- is far > less > intrusive than the transformation inherent in `jshell`, this is still > not an > ideal on-ramp.? Local variables interact with local classes (and > methods, when > we have them) in a very different way than instance fields do with > instance > methods and inner classes: their scopes are different (no forward > references), > their initialization rules are different, and captured local variables > must be > effectively final.? This is a subtly different programming model that > would then > have to be unlearned when scaling up to full classes. Further, the > result of > this wrapping -- where everything is local to the main method -- is > also not > "idiomatic Java".? So while local methods may be an attractive > feature, they are > similarly not the on-ramp we are looking for. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From angelos.bimpoudis at oracle.com Fri Oct 14 14:59:08 2022 From: angelos.bimpoudis at oracle.com (Angelos Bimpoudis) Date: Fri, 14 Oct 2022 14:59:08 +0000 Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: References: Message-ID: Hello experts! Regarding the code locations where underscore is being introduced: The EG already agreed on local variables and lambda parameters only, for now. To recap, this approach has the advantage of mostly lining up with the uses of `var` and also, that it includes code locations that deal with implementation details, as opposed to API (fields or method formal parameters). Thus, the N places where locals are declared in Java are the following: 1. the header of an enhanced for loop 2. a local variable declaration statement in a block 3. a catch formal parameter 4. a formal parameter of a lambda expression 5. a pattern variable 6. the header of a basic for statement 7. a resource specification of a try-with-resources statement The obvious place to start introducing the meaning of the underscore token is to "all of these", targeting uniformity in the language. The downside is that some of those places are pretty messy (e.g., for loops) and Java developers may not like this. So, this puts us in a position between two general principles: uniformity (support all of the above) and being seen to have introduced underscore in places where underscore shouldn't make sense. Where things are getting confusing are the following: 6. the header of a basic for statement At the moment, there is code out there that uses the for statement in weird ways, like: `for(sideEffects(), moreSideEffects(); condition() > flag;) { }` This is admittedly already a bit confusing. Do we really want to add to that the ability to use underscore? `for (var _ = sideEffects(); true; ) { ... }` That code could be refactored to: ``` var _ = sideEffects(); for (; true; ) { ... } ``` I am wondering if `_` has a place in the init list of a regular for.. 7. a resource specification of a try-with-resources statement Today we get this warning with a TWR-statement if the local is ignored: > javac -Xlint:all MyScopedLock.java MyScopedLock.java:23: warning: [try] auto-closeable resource ignored is never referenced in body of corresponding try statement try(MyScopedLock ignored = l.lock()) { ^ 1 warning So here we may have a clash of philosophies for the enhanced-for and how people use the `AutoCloseable`. Looking forward to hearing your thoughts. Best, Angelos ________________________________ From: amber-spec-experts on behalf of Angelos Bimpoudis Sent: 29 September 2022 19:03 To: amber-spec-experts Subject: Draft JEP: Unnamed local variables and patterns Dear experts, The draft JEP for unnamed local variables and patterns, that has been previously discussed on this list is available at: https://bugs.openjdk.org/browse/JDK-8294349 Comments welcomed! Angelos -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Oct 18 14:41:44 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 18 Oct 2022 16:41:44 +0200 (CEST) Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: References: Message-ID: <1964893697.28735304.1666104104465.JavaMail.zimbra@u-pem.fr> > From: "Angelos Bimpoudis" > To: "amber-spec-experts" > Sent: Friday, October 14, 2022 4:59:08 PM > Subject: Re: Draft JEP: Unnamed local variables and patterns > Hello experts! > Regarding the code locations where underscore is being introduced: The EG > already agreed on local variables and lambda parameters only, for now. To recap, > this approach has the advantage of mostly lining up with the uses of `var` and > also, that it includes code locations that deal with implementation details, as > opposed to API (fields or method formal parameters). Thus, the N places where > locals are declared in Java are the following: > 1. the header of an enhanced for loop > 2. a local variable declaration statement in a block > 3. a catch formal parameter > 4. a formal parameter of a lambda expression > 5. a pattern variable > 6. the header of a basic for statement > 7. a resource specification of a try-with-resources statement > The obvious place to start introducing the meaning of the underscore token is to > "all of these", targeting uniformity in the language. The downside is that some > of those places are pretty messy (e.g., for loops) and Java developers may not > like this. > So, this puts us in a position between two general principles: uniformity > (support all of the above) and being seen to have introduced underscore in > places where underscore shouldn't make sense. Should not make sense or can be written in a better way ? > Where things are getting confusing are the following: > 6. the header of a basic for statement > At the moment, there is code out there that uses the for statement in weird > ways, like: > `for(sideEffects(), moreSideEffects(); condition() > flag;) { }` > This is admittedly already a bit confusing. Do we really want to add to that the > ability to use underscore? > `for (var _ = sideEffects(); true; ) { ... }` > That code could be refactored to: > ``` > var _ = sideEffects(); > for (; true; ) { ... } > ``` > I am wondering if `_` has a place in the init list of a regular for.. Given that people already write things like for(boolean unused = sideEffects(); ...;) { ... } i think that replacing "unused" by '_' is a step forward. > 7. a resource specification of a try-with-resources statement > Today we get this warning with a TWR-statement if the local is ignored: > > javac -Xlint:all MyScopedLock.java > MyScopedLock.java:23: warning: [try] auto-closeable resource ignored is never > referenced in body of corresponding try statement > try(MyScopedLock ignored = l.lock()) { > ^ > 1 warning > So here we may have a clash of philosophies for the enhanced-for and how people > use the `AutoCloseable`. I think we should disallow '_' here, mostly because > Looking forward to hearing your thoughts. > Best, > Angelos > From: amber-spec-experts on behalf of > Angelos Bimpoudis > Sent: 29 September 2022 19:03 > To: amber-spec-experts > Subject: Draft JEP: Unnamed local variables and patterns > Dear experts, > The draft JEP for unnamed local variables and patterns, that has been previously > discussed on this list is available at: > https://bugs.openjdk.org/browse/JDK-8294349 > Comments welcomed! > Angelos -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Oct 18 14:47:32 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 18 Oct 2022 16:47:32 +0200 (CEST) Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: References: Message-ID: <1189988704.28742486.1666104452291.JavaMail.zimbra@u-pem.fr> the previous message was sent too fast, sorry for the inconvenience, > From: "Angelos Bimpoudis" < angelos.bimpoudis at oracle.com > > To: "amber-spec-experts" < amber-spec-experts at openjdk.java.net > > Sent: Friday, October 14 , 2022 4:59:08 PM > Subject: Re: Draft JEP: Unnamed local variables and patterns > Hello experts! > Regarding the code locations where underscore is being introduced: The EG > already agreed on local variables and lambda parameters only, for now. To recap, > this approach has the advantage of mostly lining up with the uses of `var` and > also, that it includes code locations that deal with implementation details, as > opposed to API (fields or method formal parameters). Thus, the N places where > locals are declared in Java are the following: > 1. the header of an enhanced for loop > 2. a local variable declaration statement in a block > 3. a catch formal parameter > 4. a formal parameter of a lambda expression > 5. a pattern variable > 6. the header of a basic for statement > 7. a resource specification of a try-with-resources statement > The obvious place to start introducing the meaning of the underscore token is to > "all of these", targeting uniformity in the language. The downside is that some > of those places are pretty messy (e.g., for loops) and Java developers may not > like this. > So, this puts us in a position between two general principles: uniformity > (support all of the above) and being seen to have introduced underscore in > places where underscore shouldn't make sense. Does the code make no sense or can it be written in a better way ? > Where things are getting confusing are the following: > 6. the header of a basic for statement > At the moment, there is code out there that uses the for statement in weird > ways, like: > `for(sideEffects(), moreSideEffects(); condition() > flag;) { }` > This is admittedly already a bit confusing. Do we really want to add to that the > ability to use underscore? > `for (var _ = sideEffects(); true; ) { ... }` > That code could be refactored to: > ``` > var _ = sideEffects(); > for (; true; ) { ... } > ``` > I am wondering if `_` has a place in the init list of a regular for.. Given that people already write things like for(boolean unused = sideEffects(); ...;) { ... } i think that replacing "unused" by '_' is a step forward. > 7. a resource specification of a try-with-resources statement > Today we get this warning with a TWR-statement if the local is ignored: > > javac -Xlint:all MyScopedLock.java > MyScopedLock.java:23: warning: [try] auto-closeable resource ignored is never > referenced in body of corresponding try statement > try(MyScopedLock ignored = l.lock()) { > ^ > 1 warning > So here we may have a clash of philosophies for the enhanced-for and how people > use the `AutoCloseable`. I think we should disallow '_' here, mostly because the variable is used to call close() so i think it is a good idea to maintain the idea that a try-with-resources is just a syntactic sugar on top of a try/finally that call close(). If we allow '_', it means that we are in a way able to call _.close(). The other case is the case (2), should we allow '_' in a middle of an init list, i think that like with 'var' we should not allow '_' in an init list. So reject int x, _; > Looking forward to hearing your thoughts. > Best, > Angelos regards, R?mi > From: amber-spec-experts < amber-spec-experts-retn at openjdk.org > on behalf of > Angelos Bimpoudis < angelos.bimpoudis at oracle.com > > Sent: 29 September 2022 19:03 > To: amber-spec-experts < amber-spec-experts at openjdk.java.net > > Subject: Draft JEP: Unnamed local variables and patterns > Dear experts, > The draft JEP for unnamed local variables and patterns, that has been previously > discussed on this list is available at: > [ https://bugs.openjdk.org/browse/JDK-8294349 | > https://bugs.openjdk.org/browse/JDK-8294349 ] > Comments welcomed! > Angelos -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Tue Oct 18 20:30:34 2022 From: john.r.rose at oracle.com (John Rose) Date: Tue, 18 Oct 2022 13:30:34 -0700 Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: <1189988704.28742486.1666104452291.JavaMail.zimbra@u-pem.fr> References: <1189988704.28742486.1666104452291.JavaMail.zimbra@u-pem.fr> Message-ID: <5EB235EC-5CEA-4618-9086-8C85AF9C812C@oracle.com> On 18 Oct 2022, at 7:47, Remi Forax wrote: >>> javac -Xlint:all MyScopedLock.java >> MyScopedLock.java:23: warning: [try] auto-closeable resource ignored >> is never >> referenced in body of corresponding try statement >> try(MyScopedLock ignored = l.lock()) { >> ^ >> 1 warning > >> So here we may have a clash of philosophies for the enhanced-for and >> how people >> use the `AutoCloseable`. > > I think we should disallow '_' here, mostly because the variable is > used to call close() so i think it is a good idea to maintain the idea > that a try-with-resources is just a syntactic sugar on top of a > try/finally that call close(). I think the opposite on this one. It seems to me that using `_` is an excellent way to mute that warning. I find it annoyingly opinionated: Why shouldn?t I expect to use TWR to simulate the RAII-style open/close events from C++, without lint bumping my elbow? > If we allow '_', it means that we are in a way able to call _.close(). The documentation for desugaring TWR can just introduce a new name if necessary; the JLS introduces unnamed temps all the time and this is just another place for one. > > The other case is the case (2), should we allow '_' in a middle of an > init list, i think that like with 'var' we should not allow '_' in an > init list. > So reject > int x, _; I agree. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Tue Oct 18 20:52:09 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 18 Oct 2022 16:52:09 -0400 Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: <5EB235EC-5CEA-4618-9086-8C85AF9C812C@oracle.com> References: <1189988704.28742486.1666104452291.JavaMail.zimbra@u-pem.fr> <5EB235EC-5CEA-4618-9086-8C85AF9C812C@oracle.com> Message-ID: <1b16ee1f-3d70-0993-b9b8-d6f0f865270f@oracle.com> I come to the same conclusion as John, but from a slightly different angle. First, I'm not thrilled with the idea of specifying TWR via desugaring at all; it means that accidental artifacts of specification end up being weighted too seriously.? But I also think that TWR suffers from failure of imagination (it is too IO-specific) and also that it suffers from other accidental issues (such as, using an interface for AC, which forces us to pick a name that might be great for IO but lousy for arbitrary resources.? A type class would be better, when we have them.) So I would prefer to raise our sights on TWR towards the construct we would like to work towards (just as we raised up switch from the gutter of byte-stream parsing). > I think the opposite on this one. It seems to me that using |_| is an > excellent way to mute that warning. I find it annoyingly opinionated: > Why shouldn?t I expect to use TWR to simulate the RAII-style > open/close events from C++, without lint bumping my elbow? > > If we allow '_', it means that we are in a way able to call _.close(). > > The documentation for desugaring TWR can just introduce a new name if > necessary; the JLS introduces unnamed temps all the time and this is > just another place for one. > > The other case is the case (2), should we allow '_' in a middle of > an init list, i think that like with 'var' we should not allow '_' > in an init list. > So reject > int x, _; > > I agree. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Oct 18 21:17:21 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Tue, 18 Oct 2022 23:17:21 +0200 (CEST) Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: <1b16ee1f-3d70-0993-b9b8-d6f0f865270f@oracle.com> References: <1189988704.28742486.1666104452291.JavaMail.zimbra@u-pem.fr> <5EB235EC-5CEA-4618-9086-8C85AF9C812C@oracle.com> <1b16ee1f-3d70-0993-b9b8-d6f0f865270f@oracle.com> Message-ID: <552245113.28961676.1666127841924.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "John Rose" , "Remi Forax" > Cc: "Angelos Bimpoudis" , "amber-spec-experts" > > Sent: Tuesday, October 18, 2022 10:52:09 PM > Subject: Re: Draft JEP: Unnamed local variables and patterns > I come to the same conclusion as John, but from a slightly different angle. > First, I'm not thrilled with the idea of specifying TWR via desugaring at all; > it means that accidental artifacts of specification end up being weighted too > seriously. yes, that can be an issue. > But I also think that TWR suffers from failure of imagination (it is too > IO-specific) and also that it suffers from other accidental issues (such as, > using an interface for AC, which forces us to pick a name that might be great > for IO but lousy for arbitrary resources. A type class would be better, when we > have them.) > So I would prefer to raise our sights on TWR towards the construct we would like > to work towards (just as we raised up switch from the gutter of byte-stream > parsing). In that case TWR should be available as an expression too, i would be handy by example for the StructuredTaskScope of Loom, var result = try(var scope = new StructuredTaskScope()) { var future1 = scope.fork(...); var future2 = scope.fork(...); scope.join(); yield future1.resultNow() + future2.resultNow(); }; To John: >> I think the opposite on this one. It seems to me that using _ is an excellent >> way to mute that warning. I find it annoyingly opinionated: Why shouldn?t I >> expect to use TWR to simulate the RAII-style open/close events from C++, >> without lint bumping my elbow? The idea of the actual warning is to avoid lock.lock(); try(AutoCloseable _ = lock::unlock) { ... } because using a try/finally is more readable lock.lock(); try { ... } finally { lock.unlock(); } and TWR mess with exception in case lock.unlock() is called without holding the lock and there is an exception thrown in the body of the TWR. Another way to see the issue is that TWR does not work well if the close() is not idempotent. For me TWR is too tailored to the management of IO resources, trying to re-use it for RAII is a mistake and try/finally is already the way to implement the same idea as RAII in Java. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From joe.darcy at oracle.com Tue Oct 18 21:36:15 2022 From: joe.darcy at oracle.com (Joseph D. Darcy) Date: Tue, 18 Oct 2022 14:36:15 -0700 Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: <1b16ee1f-3d70-0993-b9b8-d6f0f865270f@oracle.com> References: <1189988704.28742486.1666104452291.JavaMail.zimbra@u-pem.fr> <5EB235EC-5CEA-4618-9086-8C85AF9C812C@oracle.com> <1b16ee1f-3d70-0993-b9b8-d6f0f865270f@oracle.com> Message-ID: <819ad028-8b55-c116-3363-824957d514a3@oracle.com> On 10/18/2022 1:52 PM, Brian Goetz wrote: > I come to the same conclusion as John, but from a slightly different > angle. > > First, I'm not thrilled with the idea of specifying TWR via desugaring > at all; it means that accidental artifacts of specification end up > being weighted too seriously.? But I also think that TWR suffers from > failure of imagination (it is too IO-specific) and also that it > suffers from other accidental issues (such as, using an interface for > AC, which forces us to pick a name that might be great for IO but > lousy for arbitrary resources.? A type class would be better, when we > have them.) > > So I would prefer to raise our sights on TWR towards the construct we > would like to work towards (just as we raised up switch from the > gutter of byte-stream parsing). There were extensive discussions during the development of Project Coin / JSR 334 about using TWR for non-IO resources, in particular locks. In the end, no specific accommodation was made for non-IO types. From the JSR 334 PFD discussion: > At least in Java SE 7, a class must have a method named "close" to > implement the |AutoCloseable| interface and thus work with the > |try|-with-resources statement. To allow methods with other names like > "dispose" to be called at block exit instead, an adapter interface > with a matching factory can be used to wrap the object in question and > forward calls from "close" to the type-specific clean-up method. In > the future, it is possible other mechanisms, such as interface > injection, may also allow classes not declared to implement > |AutoCloseable| to be operated on by the |try|-with-resources statement. -Joe -------------- next part -------------- An HTML attachment was scrubbed... URL: From joe.darcy at oracle.com Tue Oct 18 21:36:15 2022 From: joe.darcy at oracle.com (Joseph D. Darcy) Date: Tue, 18 Oct 2022 14:36:15 -0700 Subject: Draft JEP: Unnamed local variables and patterns In-Reply-To: <1b16ee1f-3d70-0993-b9b8-d6f0f865270f@oracle.com> References: <1189988704.28742486.1666104452291.JavaMail.zimbra@u-pem.fr> <5EB235EC-5CEA-4618-9086-8C85AF9C812C@oracle.com> <1b16ee1f-3d70-0993-b9b8-d6f0f865270f@oracle.com> Message-ID: <819ad028-8b55-c116-3363-824957d514a3@oracle.com> On 10/18/2022 1:52 PM, Brian Goetz wrote: > I come to the same conclusion as John, but from a slightly different > angle. > > First, I'm not thrilled with the idea of specifying TWR via desugaring > at all; it means that accidental artifacts of specification end up > being weighted too seriously.? But I also think that TWR suffers from > failure of imagination (it is too IO-specific) and also that it > suffers from other accidental issues (such as, using an interface for > AC, which forces us to pick a name that might be great for IO but > lousy for arbitrary resources.? A type class would be better, when we > have them.) > > So I would prefer to raise our sights on TWR towards the construct we > would like to work towards (just as we raised up switch from the > gutter of byte-stream parsing). There were extensive discussions during the development of Project Coin / JSR 334 about using TWR for non-IO resources, in particular locks. In the end, no specific accommodation was made for non-IO types. From the JSR 334 PFD discussion: > At least in Java SE 7, a class must have a method named "close" to > implement the |AutoCloseable| interface and thus work with the > |try|-with-resources statement. To allow methods with other names like > "dispose" to be called at block exit instead, an adapter interface > with a matching factory can be used to wrap the object in question and > forward calls from "close" to the type-specific clean-up method. In > the future, it is possible other mechanisms, such as interface > injection, may also allow classes not declared to implement > |AutoCloseable| to be operated on by the |try|-with-resources statement. -Joe -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Tue Oct 18 22:22:55 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 18 Oct 2022 22:22:55 +0000 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available Message-ID: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> Dear experts: The first draft of a joint spec covering JEP 433 (Fourth Preview of Pattern Matching for switch) and JEP 432 (Record Patterns) is available at: https://cr.openjdk.java.net/~gbierman/jep432+433/latest This covers all the changes for these JEPs *apart* from the proposal to infer type arguments in patterns. This will be added and announced shortly. Comments welcomed! Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Oct 19 15:04:09 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 19 Oct 2022 11:04:09 -0400 Subject: Fwd: Draft JEP: Unnamed local variables and patterns In-Reply-To: <19CF33F2-EC59-4994-ADBA-92E5C3F3D665@unite.eu> References: <19CF33F2-EC59-4994-ADBA-92E5C3F3D665@unite.eu> Message-ID: Received on the -comments list, on the subject of unnamed locals in TWR. -------- Forwarded Message -------- Subject: Re: Draft JEP: Unnamed local variables and patterns Date: Tue, 18 Oct 2022 06:43:18 +0000 From: Till Brychcy To: amber-spec-comments at openjdk.org > 7. a resource specification of a try-with-resources statement > > Today we get this warning with a TWR-statement if the local is ignored: > > > > javac -Xlint:all MyScopedLock.java > > MyScopedLock.java:23: warning: [try] auto-closeable resource ignored is never referenced in body of corresponding try statement > try(MyScopedLock ignored = l.lock()) { > ^ > 1 warning > > So here we may have a clash of philosophies for the enhanced-for and how people > use the `AutoCloseable`. We have a similar use case: a logging framework that uses the close() call to measure the runtime of some code block. It looks like this: try (__ __ = ThreadLogger.block(SomeClass.class, "someAction")) { // some code } Note we named the helper class and variable already "__" to make clear that they are not used - before Java 8 we used to called them "_? :-) Big advantages in comparison to e.g. a lambda based solution are that this doesn?t interfere with exceptions and variables outside the try statement can be assigned. There are also more cases reported by other people in https://bugs.eclipse.org/bugs/show_bug.cgi?id=560733 , which why an already implemented warning for such variables by Eclipse was removed again. Wir?stellen?uns?als?Marke?neu?auf?und?stehen?mehr?denn?je?zusammen.? Unite?ist?nun?unsere?f?hrende?Unternehmensmarke. *Erfahren?Sie?mehr* Folgen?Sie?uns?auf *LinkedIn* , *Twitter* ?oder *YouTube* *unite.eu* Unite?Services?GmbH?&?Co.?KG,?im?Auftrag?von?Unite Unite?Network?SE Neumarkt?9,?04109?Leipzig,?Deutschland?|?Amtsgericht?Leipzig,?HRB?39278 ?Vorstand:?Dr.?Sebastian?Wieser?(Vorsitzender),?Christel?Constant,?Peter?Ledermann,?Dr.?Bernd?Sch?nw?lder ?Aufsichtsrat:?Karl?Mayer?Rieckh?(Vorsitzender) Mercateo?Deutschland?AG Mercateo?Deutschland?AG?F?rstenfelder?Stra?e?5,?80331?M?nchen,?Deutschland?|?Amtsgericht?M?nchen,?HRB?243681 ?Vorstand:?Dr.?Sebastian?Wieser?(Vorsitzender),?Christel?Constant,?Peter?Ledermann,?Dr.?Bernd?Sch?nw?lder ?Aufsichtsrat:?Karl?Mayer?Rieckh?(Vorsitzender) *mercateo.com/procure* -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: image985586.png Type: image/png Size: 4303 bytes Desc: not available URL: From brian.goetz at oracle.com Wed Oct 19 16:43:18 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 19 Oct 2022 12:43:18 -0400 Subject: Paving the on-ramp In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> Message-ID: <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> Discussion here seems to have settled down.? Here's what seem like the possibly-open parts: ?- Most of the concerns centered around the imports part, not the main part.? We should think some more about alternatives for Console IO other than auto-importing a static println method; this might mean that their invocation is something more verbose than just "println", but better than "System.out.println", and possibly more discoverable.? In the meantime, we'll separate off the auto-import for STR as part of string templates to decouple these trains. ?- The notion of "unnamed class" seems to fill a hole alongside "unnamed package" and "unnamed module", but the analogy is imperfect, because these classes _will_ have a name, just not one visible from _inside_ the class.? The alternative is to view these as _implicitly named_ classes, where they have a name, just derived from the file system rather than the source code. The distinction might influence whether such simplified class declarations can have constructors (if you can't name it, you can't name the constructor), whether static methods or fields can be accessed from within the class, etc.? (However, these influences are weak; we could still reasonably choose to disallow constructors and instance initializers even if the name is denotable.) We could, of course, prevent class access from outside the class too, effectively making such classes visible only to the launcher.? But this would come at no small cost to prevent such accesses, and for limited benefit.? The status-quo proposal currently says: the name of the class is not visible from within the class, you can't have constructors or instance initializers, you get an implicit no-arg constructor like every other constructor-less class.? If you want a more complex construction protocol, or supertypes, or class declaration annotations, or to live in a package, declare a class.? This still seems pretty OK. On 10/12/2022 7:56 PM, Brian Goetz wrote: > I've gotten some good feedback from this writeup, much of it > illuminating how different people (myself included) make different > assumptions about how certain things work / what things are for. > > #### Role of main > > A main() method can be viewed from two perspectives: it is an ordinary > method with modifiers, parameters, etc, but it is also an externally > significant _entry point_ into a program. (We've seen a similar > double-duty with method parameters in the context of the underscore > discussion: a method parameter is both a "local" that serves the > implementation of the method, but its also part of the specification > of the method (e.g., the name and type bleed into the Javadoc.)) > > Java made the choice in 1995 that an entry point is "just" a suitably > named / structured / accessible method.? This was somewhat > "un-Java-ish", as identification of main-ness is a structural > property, and Java (certainly back then) mostly avoided such > structural techniques where it could.? I think its fair to question > whether this decision is still serving us (Stephen's proposal does > exactly that), but I think this was a pragmatic choice and I'm > inclined to continue to run with it, rather than look at it as > something "to be fixed". > > So the proposal broadens the structure parameters around "what is a > main", but ultimately leaves the "some methods are special" decision > in place.? (Aside: if we'd had annotations in Java 1.0, we might well > have used an @EntryPoint capture the metadata of "this method is > externally launchable", and let the launcher reflect over the methods > of a class to find a suitable entry point.? But we didn't, and trying > to rewrite history in this way is likely a losing game.) > > #### Role of launcher > > This raises a secondary question about the role of the launcher.? My > mental model was one of "the launcher is just some out-of-sight code > that invokes the main method."? That `main` (currently) should be > public plays into this, because if this out-of-sight code is going to > call this method, surely accessibility will have a chance to say "can > this code call this method".? I was surprised, though, to find out > that others have a different view of this.? Kevin asked whether it was > a "bug" in the model that, in order for the launcher to be able to > call a `main` method, then so must other code.? This is a form of > coupling that I had assumed is pure good (because it means there's a > unified model of invocation), but is reasonable to call it out as a > coupling. > > In my view, treating the launcher as "just another caller" simplifies > the story, because we don't have to have a separate story for invoking > entry points.? If we're doubling down on "main is just a method with > some special metadata that says 'start here'", then doubling down on > accessibility here as well makes sense to me also.? Hence my > suggestion to frame the launcher as "ordinary code in the unnamed > package".? But as natural as this seems to me, I recognize that this > may seem weird to others. > > #### Role of unnamed packages > > Another place where there were differing viewpoints is on the role of > unnamed packages.? Some felt that using the unnamed package at all > should be reserved for the most transient of "run it once and delete > it" programs; others felt that it had a role beyond mere experiments. > > In part because of the difficulties described in the On Ramp document, > Java has not found extensive use in scripts, but we are hoping that > this will become more common over time.? (The "Launch Single File Java > Programs" JEP was a step towards being more script-friendly.)? I would > think that the unnamed package is the right place for scripts as well, > since users will want to identify the program by its path in the file > system, not by setting up a class path and referring to a relative > position within that. > > Overall, I think the result of this project will be that *more* code > lives in the unnamed package -- and that's OK, because people will > write more simple, self-contained programs in Java, and the unnamed > package is an OK place for these things to live. > > #### Instance main and interfaces > > Several folks have pointed out that perhaps an instance main should be > specified by an interface (say, Program), and that unnamed classes > implicitly implement Program.? This has some attraction, since this is > generally how we prevent names from being magic. > > There are some downsides, though.? We already have two ways to spell > "main"; we might want more in the future, and having to spell them all > out in an interface (even with defaults) could impede evolution.? More > importantly, this is using a different mechanism that we use for the > main we currently have, which means there are two different ways to > identify the two kinds of main.? This is starting to veer into the > sort of "fix mistakes of the past" siren song which leaves you with > two different ways of doing the same thing, which appears to new users > as more complexity, not less. > > #### Moral hazards > > A significant proportion of the negative feedback to the proposal > amounted to some sort of "moral hazard".? The biggest component here > was that shortening `System.out.println()` to `println()` would > encourage people to use console IO more often, in places where it was > not appropriate, or that it would be an impediment to testing.? I > didn't find this argument very compelling, though; I don't think the > number of characters required to access console IO is the determining > factor here in choosing an IO strategy (or even being aware that there > is such a thing as an IO strategy.) > > #### Console IO > > There were a variety of opinions on the direction we should go with > simplifying console IO.? While nearly everyone agrees that > `System.out.println` is a mouthful, there were a number of alternate > approaches suggested, such as: > > ?- Expose new classes (e.g., SystemOut) with static methods > (SystemOut.println) instead of new static methods; > ?- Expose a new API for console IO, but don't auto-import anything; > ?- Auto-import console IO methods only in unnamed classes; > ?- others. > > #### Auto imports > > After some thought, I realize that the auto-import feature is trying > to serve two separate goals, and maybe is getting distorted because of > it.? The String Templates JEP defines some canned processors (STR and > FMT), which are effectively part of the language.? At first blush > (which is when people are often most vocal about peripheral features), > people already don't like having to type STR-dot (sorry, not changing > our mind about that), and having to import STR feels like adding > insult to injury.? STR is effectively part of the language, so we > don't want to place hoops in the way of using it.? But that is a > slightly different use case than rescuing println, for which there are > multiple possible paths.? Maybe its best to separate these. > > > The On Ramp document outlined some (admittedly fuzzy) goals which may > be helpful for evaluating some of these thoughts. We are not trying to > create a "beginner's dialect of Java", nor am I particularly > interested in using this effort to turn back the clock and redesign > features that some people wish had been designed otherwise.? The goal > here is, to the extent possible, to make elements of program > declaration recede into the background until they are actually adding > value.? Access control, classes, statics, etc, should come into the > picture when they are needed, and not before.? I'm sure that there are > other aspects of the language that people would like to have recede > into the background too, and will attempt to attach to this project, > but I'm interested in focusing primarily on those that are impediments > to learning the language.? If we do our job right, these will have > benefits well beyond the first few days of using Java. > > My thoughts: > > ?- Java made the choice that "entry points" are specially structured > methods, and can be called both programmatically and via the > launcher.? I see no reason to tinker with this concept, or to > materially change how entry points are declared -- just to widen the > range of `main` methods that are considered entry points. > > ?- While the JLS doesn't really say what the launcher is (just that > there is something that launches programs), positioning the launcher > as some code in the unnamed package seems helpful, because familiar > concepts of access control apply both to programmatic and command line > invocation. > > ?- While I get that some people find the unnamed package to be a > "smell", I think it is actually fine for a range of programs, and I > expect that range to broaden over time as people start using Java for > scripts. > > ?- I am sympathetic to the argument that people don't want "println" > and friends to effectively become global; this is separate from > simplifying the locution.? Where there may be an attractive middle > ground is: > > ??? - Take the new methods for console IO and put them somewhere in > java.lang, say ConsoleIO; > ??? - Auto-import ConsoleIO only in unnamed classes. > > While this creates a new coupling, it may align well, in that the > programs who most want access to console IO are those that are most > likely to be able to be unnamed classes -- and the rest can import all > the console IO with a single `import static ConsoleIO.*`.? Refactoring > to a named class would then involve adding both the class wrapper and > this import. > > ?- I am open to other approaches for improving the console IO story.? > Maybe this is Console.println(), which avoids the need for > auto-static-import at all at the cost of some more words (but at least > doesn't require understanding static fields, and has the advantage > that all the console stuff can be in one place), or maybe something > else.? (A big part of the problem with the status quo now is that not > only is System.out.println undiscoverable, but having found that, you > still have no idea where "readln" is.) > > ?- While I get why people are attracted to putting instance main in an > interface, I don't think I'd like the result.? As mentioned above, > this implicitly places an impediment in future expansion of the > launcher protocol, but more importantly, means we have two mechanisms > for finding main -- an "old" and "new" one.? I try hard to avoid > creating this situation, because the benefit is rarely worth the > increased cognitive load. > > > > > > On 9/28/2022 1:57 PM, Brian Goetz wrote: >> At various points, we've explored the question of which program >> elements are most and least helpful for students first learning >> Java.? After considering a number of alternatives over the years, I >> have a simple proposal for smoothing the "on ramp" to Java >> programming, while not creating new things to unlearn. >> >> Markdown source is below, HTML will appear soon at: >> >> https://openjdk.org/projects/amber/design-notes/on-ramp >> >> >> # Paving the on-ramp >> >> Java is one of the most widely taught programming languages in the >> world.? Tens >> of thousands of educators find that the imperative core of the >> language combined >> with a straightforward standard library is a foundation that students can >> comfortably learn on.? Choosing Java gives educators many degrees of >> freedom: >> they can situate students in `jshell` or Notepad or a full-fledged >> IDE; they can >> teach imperative, object-oriented, functional, or hybrid programming >> styles; and >> they can easily find libraries to interact with external data and >> services. >> >> No language is perfect, and one of the most common complaints about >> Java is that >> it is "too verbose" or has "too much ceremony."? And unfortunately, >> Java imposes >> its heaviest ceremony on those first learning the language, who need and >> appreciate it the least.? The declaration of a class and the >> incantation of >> `public static void main` is pure mystery to a beginning programmer.? >> While >> these incantations have principled origins and serve a useful >> organizing purpose >> in larger programs, they have the effect of placing obstacles in the >> path of >> _becoming_ Java programmers. Educators constantly remind us of the >> litany of >> complexity that students have to confront on Day 1 of class -- when >> they really >> just want to write their first program. >> >> As an amusing demonstration of this, in her JavaOne keynote >> appearance in 2019, >> [Aimee Lucido](https://www.youtube.com/watch?v=BkPPFiXUwYk) talked >> about when >> she learned to program in Java, and how her teacher performed a rap song >> to help students memorize `"public static void main"`.? Our hats are >> off to >> creative educators everywhere for this kind of dedication, but teachers >> shouldn't have to do this. >> >> Of course, advanced programmers complain about ceremony too.? We will >> never be >> able to satisfy programmers' insatiable appetite for typing fewer >> keystrokes, >> and we shouldn't try, because the goal of programming is to write >> programs that >> are easy to read and are clearly correct, not programs that were easy >> to type. >> But we can try to better align the ceremony commensurate with the >> value it >> brings to a program -- and let simple programs be expressed more simply. >> >> ## Concept overload >> >> The classic "Hello World" program looks like this in Java: >> >> ``` >> public class HelloWorld { >> ??? public static void main(String[] args) { >> ??????? System.out.println("Hello World"); >> ??? } >> } >> ``` >> >> It may only be five lines, but those lines are packed with concepts >> that are >> challenging to absorb without already having some programming >> experience and >> familiarity with object orientation. Let's break down the concepts a >> student >> confronts when writing their first Java program: >> >> ? - **public** (on the class).? The `public` accessibility level is >> relevant >> ??? only when there is going to be cross-package access; in a simple >> "Hello >> ??? World" program, there is only one class, which lives in the >> unnamed package. >> ??? They haven't even written a one-line program yet; the notion of >> access >> ??? control -- keeping parts of a program from accessing other parts >> of it -- is >> ??? still way in their future. >> >> ? - **class**.? Our student hasn't set out to write a _class_, or model a >> ??? complex system with objects; they want to write a _program_.? In >> Java, a >> ??? program is just a `main` method in some class, but at this point >> our student >> ??? still has no idea what a class is or why they want one. >> >> ? - **Methods**.? Methods are of course a key concept in Java, but >> the mechanics >> ??? of methods -- parameters, return types, and invocation -- are still >> ??? unfamiliar, and the `main` method is invoked magically from the >> `java` >> ??? launcher rather than from explicit code. >> >> ? - **public** (again).? Like the class, the `main` method has to be >> public, but >> ??? again this is only relevant when programs are large enough to require >> ??? packages to organize them. >> >> ? - **static**.? The `main` method has to be static, and at this >> point, students >> ??? have no context for understanding what a static method is or why >> they want >> ??? one.? Worse, the early exposure to `static` methods will turn out >> to be a >> ??? bad habit that must be later unlearned.? Worse still, the fact >> that the >> ??? `main` method is `static` creates a seam between `main` and other >> methods; >> ??? either they must become `static` too, or the `main` method must >> trampoline >> ??? to some sort of "instance main" (more ceremony!)? And if we get >> this wrong, >> ??? we get the dreaded and mystifying `"cannot be referenced from a >> static >> ??? context"` error. >> >> ? - **main**.? The name `main` has special meaning in a Java program, >> indicating >> ??? the starting point of a program, but this specialness hides >> behind being an >> ??? ordinary method name.? This may contribute to the sense of "so >> many magic >> ??? incantations." >> >> ? - **String[]**.? The parameter to `main` is an array of strings, >> which are the >> ??? arguments that the `java` launcher collected from the command >> line.? But our >> ??? first program -- likely our first dozen -- will not use command-line >> ??? parameters. Requiring the `String[]` parameter is, at this point, >> a mistake >> ??? waiting to happen, and it will be a long time until this >> parameter makes >> ??? sense.? Worse, educators may be tempted to explain arrays at this >> point, >> ??? which further increases the time-to-first-program. >> >> ? - **System.out.println**.? If you look closely at this incantation, >> each >> ??? element in the chain is a different thing -- `System` is a class >> (what's a >> ??? class again?), `out` is a static field (what's a field?), and >> `println` is >> ??? an instance method.? The only part the student cares about right >> now is >> ??? `println`; the rest of it is an incantation that they do not yet >> understand >> ??? in order to get at the behavior they want. >> >> That's a lot to explain to a student on the first day of class.? >> There's a good >> chance that by now, class is over and we haven't written any programs >> yet, or >> the teacher has said "don't worry what this means, you'll understand >> it later" >> six or eight times.? Not only is this a lot of _syntactic_ things to >> absorb, but >> each of those things appeals to a different concept (class, method, >> package, >> return value, parameter, array, static, public, etc) that the student >> doesn't >> have a framework for understanding yet.? Each of these will have an >> important >> role to play in larger programs, but so far, they only contribute to >> "wow, >> programming is complicated." >> >> It won't be practical (or even desirable) to get _all_ of these >> concepts out of >> the student's face on day 1, but we can do a lot -- and focus on the >> ones that >> do the most to help beginners understand how programs are constructed. >> >> ## Goal: a smooth on-ramp >> >> As much as programmers like to rant about ceremony, the real goal >> here is not >> mere ceremony reduction, but providing a graceful _on ramp_ to Java >> programming. >> This on-ramp should be helpful to beginning programmers by requiring >> only those >> concepts that a simple program needs. >> >> Not only should an on-ramp have a gradual slope and offer enough >> acceleration >> distance to get onto the highway at the right speed, but its >> direction must >> align with that of the highway.? When a programmer is ready to learn >> about more >> advanced concepts, they should not have to discard what they've >> already learned, >> but instead easily see how the simple programs they've already written >> generalize to more complicated ones, and both the syntatic and conceptual >> transformation from "simple" to "full blown" program should be >> straightforward >> and unintrusive.? It is a definite non-goal to create a "simplified >> dialect of >> Java for students". >> >> We identify three simplifications that should aid both educators and >> students in >> navigating the on-ramp to Java, as well as being generally useful to >> simple >> programs beyond the classroom as well: >> >> ?- A more tolerant launch protocol >> ?- Unnamed classes >> ?- Predefined static imports for the most critical methods and fields >> >> ## A more tolerant launch protocol >> >> The Java Language Specification has relatively little to say about >> how Java >> "programs" get launched, other than saying that there is some way to >> indicate >> which class is the initial class of a program (JLS 12.1.1) and that a >> public >> static method called `main` whose sole argument is of type `String[]` >> and whose >> return is `void` constitutes the entry point of the indicated class. >> >> We can eliminate much of the concept overload simply by relaxing the >> interactions between a Java program and the `java` launcher: >> >> ?- Relax the requirement that the class, and `main` method, be >> public.? Public >> ?? accessibility is only relevant when access crosses packages; >> simple programs >> ?? live in the unnamed package, so cannot be accessed from any other >> package >> ?? anyway.? For a program whose main class is in the unnamed package, >> we can >> ?? drop the requirement that the class or its `main` method be public, >> ?? effectively treating the `java` launcher as if it too resided in >> the unnamed >> ?? package. >> >> ?- Make the "args" parameter to `main` optional, by allowing the >> `java` launcher to >> ?? first look for a main method with the traditional `main(String[])` >> ?? signature, and then (if not found) for a main method with no >> arguments. >> >> ?- Make the `static` modifier on `main` optional, by allowing the >> `java` launcher to >> ?? invoke an instance `main` method (of either signature) by >> instantiating an >> ?? instance using an accessible no-arg constructor and then invoking >> the `main` >> ?? method on it. >> >> This small set of changes to the launch protocol strikes out five of >> the bullet >> points in the above list of concepts: public (twice), static, method >> parameters, >> and `String[]`. >> >> At this point, our Hello World program is now: >> >> ``` >> class HelloWorld { >> ??? void main() { >> ??????? System.out.println("Hello World"); >> ??? } >> } >> ``` >> >> It's not any shorter by line count, but we've removed a lot of >> "horizontal >> noise" along with a number of concepts.? Students and educators will >> appreciate >> it, but advanced programmers are unlikely to be in any hurry to make >> these >> implicit elements explicit either. >> >> Additionally, the notion of an "instance main" has value well beyond >> the first >> day.? Because excessive use of `static` is considered a code smell, many >> educators encourage the pattern of "all the static `main` method does is >> instantiate an instance and call an instance `main` method" anyway.? >> Formalizing >> the "instance main" protocol reduces a layer of boilerplate in these >> cases, and >> defers the point at which we have to explain what instance creation >> is -- and >> what `static` is.? (Further, allowing the `main` method to be an >> instance method >> means that it could be inherited from a superclass, which is useful >> for simple >> frameworks such as test runners or service frameworks.) >> >> ## Unnamed classes >> >> In a simple program, the `class` declaration often doesn't help >> either, because >> other classes (if there are any) are not going to reference it by >> name, and we >> don't extend a superclass or implement any interfaces.? If we say an >> "unnamed >> class" consists of member declarations without a class header, then >> our Hello >> World program becomes: >> >> ``` >> void main() { >> ??? System.out.println("Hello World"); >> } >> ``` >> >> Such source files can still have fields, methods, and even nested >> classes, so >> that as a program evolves from a few statements to needing some >> ancillary state >> or helper methods, these can be factored out of the `main` method >> while still >> not yet requiring a full class declaration: >> >> ``` >> String greeting() { return "Hello World"; } >> >> void main() { >> ??? System.out.println(greeting()); >> } >> ``` >> >> This is where treating `main` as an instance method really shines; >> the user has >> just declared two methods, and they can freely call each other.? >> Students need >> not confront the confusing distinction between instance and static >> methods yet; >> indeed, if not forced to confront static members on day 1, it might >> be a while >> before they do have to learn this distinction.? The fact that there is a >> receiver lurking in the background will come in handy later, but >> right now is >> not bothering anybody. >> >> [JEP 330](https://openjdk.org/jeps/330) allows single-file programs to be >> launched directly without compilation; this streamlined launcher >> pairs well with >> unnamed classes. >> >> ## Predefined static imports >> >> The most important classes, such as `String` and `Integer`, live in the >> `java.lang` package, which is automatically on-demand imported into all >> compilation units; this is why we do not have to `import >> java.lang.String` in >> every class.? Static imports were not added until Java 5, but no >> corresponding >> facility for automatic on-demand import of common behavior was added >> at that >> time.? Most programs, however, will want to do console IO, and Java >> forces us to >> do this in a roundabout way -- through the static `System.out` and >> `System.in` >> fields.? Basic console input and output is a reasonable candidate for >> auto-static import, as one or both are needed by most simple >> programs.? While >> these are currently instance methods accessed through static fields, >> we can >> easily create static methods for `println` and `readln` which are >> suitable for >> static import, and automatically import them.? At which point our >> first program >> is now down to: >> >> ``` >> void main() { >> ??? println("Hello World"); >> } >> ``` >> >> ## Putting this all together >> >> We've discussed several simplifications: >> >> ?- Update the launcher protocol to make public, static, and arguments >> optional >> ?? for main methods, and for main methods to be instance methods (when a >> ?? no-argument constructor is available); >> ?- Make the class wrapper for "main classes" optional (unnamed classes); >> ?- Automatically static import methods like `println` >> >> which together whittle our long list of day-1 concepts down >> considerably.? While >> this is still not as minimal as the minimal Python or Ruby program -- >> statements >> must still live in a method -- the goal here is not to win at "code >> golf".? The >> goal is to ensure that concepts not needed by simple programs need >> not appear in >> those programs, while at the same time not encouraging habits that >> have to be >> unlearned as programs scale up. >> >> Each of these simplifications is individually small and unintrusive, >> and each is >> independent of the others.? And each embodies a simple transformation >> that the >> author can easily manually reverse when it makes sense to do so: elided >> modifiers and `main` arguments can be added back, the class wrapper >> can be added >> back when the affordances of classes are needed (supertypes, >> constructors), and >> the full qualifier of static-import can be added back.? And these >> reversals are >> independent of one another; they can done in any combination or any >> order. >> >> This seems to meet the requirements of our on-ramp; we've eliminated >> most of the >> day-1 ceremony elements without introducing new concepts that need to be >> unlearned. The remaining concepts -- a method is a container for >> statements, and >> a program is a Java source file with a `main` method -- are easily >> understood in >> relation to their fully specified counterparts. >> >> ## Alternatives >> >> Obviously, we've lived with the status quo for 25+ years, so we could >> continue >> to do so.? There were other alternatives explored as well; >> ultimately, each of >> these fell afoul of one of our goals. >> >> ### Can't we go further? >> >> Fans of "code golf" -- of which there are many -- are surely right >> now trying to >> figure out how to eliminate the last little bit, the `main` method, >> and allow >> statements to exist at the top-level of a program.? We deliberately >> stopped >> short of this because it offers little value beyond the first few >> minutes, and >> even that small value quickly becomes something that needs to be >> unlearned. >> >> The fundamental problem behind allowing such "loose" statements is that >> variables can be declared inside both classes (fields) and methods (local >> variables), and they share the same syntactic production but not the same >> semantics.? So it is unclear (to both compilers and humans) whether a >> "loose" >> variable would be a local or a field.? If we tried to adopt some sort >> of simple >> heuristic to collapse this ambiguity (e.g., whether it precedes or >> follows the >> first statement), that may satisfy the compiler, but now simple >> refactorings >> might subtly change the meaning of the program, and we'd be replacing the >> explicit syntactic overhead of `void main()` with an invisible "line" >> in the >> program that subtly affects semantics, and a new subtle rule about >> the meaning >> of variable declarations that applies only to unnamed classes.? This >> doesn't >> help students, nor is this particularly helpful for all but the most >> trivial >> programs.? It quickly becomes a crutch to be discarded and unlearned, >> which >> falls afoul of our "on ramp" goals.? Of all the concepts on our list, >> "methods" >> and "a program is specified by a main method" seem the ones that are >> most worth >> asking students to learn early. >> >> ### Why not "just" use `jshell`? >> >> While JShell is a great interactive tool, leaning too heavily on it >> as an onramp >> would fall afoul of our goals.? A JShell session is not a program, but a >> sequence of code snippets.? When we type declarations into `jshell`, >> they are >> viewed as implicitly static members of some unspecified class, with >> accessibility is ignored completely, and statements execute in a >> context where >> all previous declarations are in scope.? This is convenient for >> experimentation >> -- the primary goal of `jshell` -- but not such a great mental model for >> learning to write Java programs.? Transforming a batch of working >> declarations >> in `jshell` to a real Java program would not be sufficiently simple or >> unintrusive, and would lead to a non-idiomatic style of code, because the >> straightforward translation would have us redeclaring each method, >> class, and >> variable declaration as `static`.? Further, this is probably not the >> direction >> we want to go when we scale up from a handful of statements and >> declarations to >> a simple class -- we probably want to start using classes as classes, >> not just >> as containers for static members. JShell is a great tool for >> exploration and >> debugging, and we expect many educators will continue to incorporate >> it into >> their curriculum, but is not the on-ramp programming model we are >> looking for. >> >> ### What about "always local"? >> >> One of the main tensions that `main` introduces is that most class >> members are >> not `static`, but the `main` method is -- and that forces programmers to >> confront the seam between static and non-static members. JShell >> answers this >> with "make everything static". >> >> Another approach would be to "make everything local" -- treat a >> simple program >> as being the "unwrapped" body of an implicit main method. We already >> allow >> variables and classes to be declared local to a method.? We could add >> local >> methods (a useful feature in its own right) and relax some of the >> asymmetries >> around nesting (again, an attractive cleanup), and then treat a mix of >> declarations and statements without a class wrapper as the body of an >> invisible >> `main` method. This seems an attractive model as well -- at first. >> >> While the syntactic overhead of converting back to full-blown classes >> -- wrap >> the whole thing in a `main` method and a `class` declaration -- is >> far less >> intrusive than the transformation inherent in `jshell`, this is still >> not an >> ideal on-ramp.? Local variables interact with local classes (and >> methods, when >> we have them) in a very different way than instance fields do with >> instance >> methods and inner classes: their scopes are different (no forward >> references), >> their initialization rules are different, and captured local >> variables must be >> effectively final.? This is a subtly different programming model that >> would then >> have to be unlearned when scaling up to full classes. Further, the >> result of >> this wrapping -- where everything is local to the main method -- is >> also not >> "idiomatic Java".? So while local methods may be an attractive >> feature, they are >> similarly not the on-ramp we are looking for. >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Oct 19 17:50:19 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 19 Oct 2022 19:50:19 +0200 (CEST) Subject: Paving the on-ramp In-Reply-To: <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> Message-ID: <276656784.29954930.1666201819779.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "amber-spec-experts" > Sent: Wednesday, October 19, 2022 6:43:18 PM > Subject: Re: Paving the on-ramp > Discussion here seems to have settled down. Here's what seem like the > possibly-open parts: > - Most of the concerns centered around the imports part, not the main part. We > should think some more about alternatives for Console IO other than > auto-importing a static println method; this might mean that their invocation > is something more verbose than just "println", but better than > "System.out.println", and possibly more discoverable. In the meantime, we'll > separate off the auto-import for STR as part of string templates to decouple > these trains. > - The notion of "unnamed class" seems to fill a hole alongside "unnamed package" > and "unnamed module", but the analogy is imperfect, because these classes > _will_ have a name, just not one visible from _inside_ the class. The > alternative is to view these as _implicitly named_ classes, where they have a > name, just derived from the file system rather than the source code. The > distinction might influence whether such simplified class declarations can have > constructors (if you can't name it, you can't name the constructor), whether > static methods or fields can be accessed from within the class, etc. (However, > these influences are weak; we could still reasonably choose to disallow > constructors and instance initializers even if the name is denotable.) > We could, of course, prevent class access from outside the class too, > effectively making such classes visible only to the launcher. But this would > come at no small cost to prevent such accesses, and for limited benefit. We can cheaply disallow access by - only allowing one compilation unit if there is an unnamed class. - mark the generated class as synthetic (see JVMS 4.7.8) so it does not work with separate compilation. > The status-quo proposal currently says: the name of the class is not visible > from within the class, you can't have constructors or instance initializers, > you get an implicit no-arg constructor like every other constructor-less class. > If you want a more complex construction protocol, or supertypes, or class > declaration annotations, or to live in a package, declare a class. This still > seems pretty OK. I still think that allowing fields inside an unamed class is a mistake (if we can avoid to talk about null at the beginning, it's a win). R?mi https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-4.html#jvms-4.7.8 > On 10/12/2022 7:56 PM, Brian Goetz wrote: >> I've gotten some good feedback from this writeup, much of it illuminating how >> different people (myself included) make different assumptions about how certain >> things work / what things are for. >> #### Role of main >> A main() method can be viewed from two perspectives: it is an ordinary method >> with modifiers, parameters, etc, but it is also an externally significant >> _entry point_ into a program. (We've seen a similar double-duty with method >> parameters in the context of the underscore discussion: a method parameter is >> both a "local" that serves the implementation of the method, but its also part >> of the specification of the method (e.g., the name and type bleed into the >> Javadoc.)) >> Java made the choice in 1995 that an entry point is "just" a suitably named / >> structured / accessible method. This was somewhat "un-Java-ish", as >> identification of main-ness is a structural property, and Java (certainly back >> then) mostly avoided such structural techniques where it could. I think its >> fair to question whether this decision is still serving us (Stephen's proposal >> does exactly that), but I think this was a pragmatic choice and I'm inclined to >> continue to run with it, rather than look at it as something "to be fixed". >> So the proposal broadens the structure parameters around "what is a main", but >> ultimately leaves the "some methods are special" decision in place. (Aside: if >> we'd had annotations in Java 1.0, we might well have used an @EntryPoint >> capture the metadata of "this method is externally launchable", and let the >> launcher reflect over the methods of a class to find a suitable entry point. >> But we didn't, and trying to rewrite history in this way is likely a losing >> game.) >> #### Role of launcher >> This raises a secondary question about the role of the launcher. My mental model >> was one of "the launcher is just some out-of-sight code that invokes the main >> method." That `main` (currently) should be public plays into this, because if >> this out-of-sight code is going to call this method, surely accessibility will >> have a chance to say "can this code call this method". I was surprised, though, >> to find out that others have a different view of this. Kevin asked whether it >> was a "bug" in the model that, in order for the launcher to be able to call a >> `main` method, then so must other code. This is a form of coupling that I had >> assumed is pure good (because it means there's a unified model of invocation), >> but is reasonable to call it out as a coupling. >> In my view, treating the launcher as "just another caller" simplifies the story, >> because we don't have to have a separate story for invoking entry points. If >> we're doubling down on "main is just a method with some special metadata that >> says 'start here'", then doubling down on accessibility here as well makes >> sense to me also. Hence my suggestion to frame the launcher as "ordinary code >> in the unnamed package". But as natural as this seems to me, I recognize that >> this may seem weird to others. >> #### Role of unnamed packages >> Another place where there were differing viewpoints is on the role of unnamed >> packages. Some felt that using the unnamed package at all should be reserved >> for the most transient of "run it once and delete it" programs; others felt >> that it had a role beyond mere experiments. >> In part because of the difficulties described in the On Ramp document, Java has >> not found extensive use in scripts, but we are hoping that this will become >> more common over time. (The "Launch Single File Java Programs" JEP was a step >> towards being more script-friendly.) I would think that the unnamed package is >> the right place for scripts as well, since users will want to identify the >> program by its path in the file system, not by setting up a class path and >> referring to a relative position within that. >> Overall, I think the result of this project will be that *more* code lives in >> the unnamed package -- and that's OK, because people will write more simple, >> self-contained programs in Java, and the unnamed package is an OK place for >> these things to live. >> #### Instance main and interfaces >> Several folks have pointed out that perhaps an instance main should be specified >> by an interface (say, Program), and that unnamed classes implicitly implement >> Program. This has some attraction, since this is generally how we prevent names >> from being magic. >> There are some downsides, though. We already have two ways to spell "main"; we >> might want more in the future, and having to spell them all out in an interface >> (even with defaults) could impede evolution. More importantly, this is using a >> different mechanism that we use for the main we currently have, which means >> there are two different ways to identify the two kinds of main. This is >> starting to veer into the sort of "fix mistakes of the past" siren song which >> leaves you with two different ways of doing the same thing, which appears to >> new users as more complexity, not less. >> #### Moral hazards >> A significant proportion of the negative feedback to the proposal amounted to >> some sort of "moral hazard". The biggest component here was that shortening >> `System.out.println()` to `println()` would encourage people to use console IO >> more often, in places where it was not appropriate, or that it would be an >> impediment to testing. I didn't find this argument very compelling, though; I >> don't think the number of characters required to access console IO is the >> determining factor here in choosing an IO strategy (or even being aware that >> there is such a thing as an IO strategy.) >> #### Console IO >> There were a variety of opinions on the direction we should go with simplifying >> console IO. While nearly everyone agrees that `System.out.println` is a >> mouthful, there were a number of alternate approaches suggested, such as: >> - Expose new classes (e.g., SystemOut) with static methods (SystemOut.println) >> instead of new static methods; >> - Expose a new API for console IO, but don't auto-import anything; >> - Auto-import console IO methods only in unnamed classes; >> - others. >> #### Auto imports >> After some thought, I realize that the auto-import feature is trying to serve >> two separate goals, and maybe is getting distorted because of it. The String >> Templates JEP defines some canned processors (STR and FMT), which are >> effectively part of the language. At first blush (which is when people are >> often most vocal about peripheral features), people already don't like having >> to type STR-dot (sorry, not changing our mind about that), and having to import >> STR feels like adding insult to injury. STR is effectively part of the >> language, so we don't want to place hoops in the way of using it. But that is a >> slightly different use case than rescuing println, for which there are multiple >> possible paths. Maybe its best to separate these. >> The On Ramp document outlined some (admittedly fuzzy) goals which may be helpful >> for evaluating some of these thoughts. We are not trying to create a >> "beginner's dialect of Java", nor am I particularly interested in using this >> effort to turn back the clock and redesign features that some people wish had >> been designed otherwise. The goal here is, to the extent possible, to make >> elements of program declaration recede into the background until they are >> actually adding value. Access control, classes, statics, etc, should come into >> the picture when they are needed, and not before. I'm sure that there are other >> aspects of the language that people would like to have recede into the >> background too, and will attempt to attach to this project, but I'm interested >> in focusing primarily on those that are impediments to learning the language. >> If we do our job right, these will have benefits well beyond the first few days >> of using Java. >> My thoughts: >> - Java made the choice that "entry points" are specially structured methods, and >> can be called both programmatically and via the launcher. I see no reason to >> tinker with this concept, or to materially change how entry points are declared >> -- just to widen the range of `main` methods that are considered entry points. >> - While the JLS doesn't really say what the launcher is (just that there is >> something that launches programs), positioning the launcher as some code in the >> unnamed package seems helpful, because familiar concepts of access control >> apply both to programmatic and command line invocation. >> - While I get that some people find the unnamed package to be a "smell", I think >> it is actually fine for a range of programs, and I expect that range to broaden >> over time as people start using Java for scripts. >> - I am sympathetic to the argument that people don't want "println" and friends >> to effectively become global; this is separate from simplifying the locution. >> Where there may be an attractive middle ground is: >> - Take the new methods for console IO and put them somewhere in java.lang, say >> ConsoleIO; >> - Auto-import ConsoleIO only in unnamed classes. >> While this creates a new coupling, it may align well, in that the programs who >> most want access to console IO are those that are most likely to be able to be >> unnamed classes -- and the rest can import all the console IO with a single >> `import static ConsoleIO.*`. Refactoring to a named class would then involve >> adding both the class wrapper and this import. >> - I am open to other approaches for improving the console IO story. Maybe this >> is Console.println(), which avoids the need for auto-static-import at all at >> the cost of some more words (but at least doesn't require understanding static >> fields, and has the advantage that all the console stuff can be in one place), >> or maybe something else. (A big part of the problem with the status quo now is >> that not only is System.out.println undiscoverable, but having found that, you >> still have no idea where "readln" is.) >> - While I get why people are attracted to putting instance main in an interface, >> I don't think I'd like the result. As mentioned above, this implicitly places >> an impediment in future expansion of the launcher protocol, but more >> importantly, means we have two mechanisms for finding main -- an "old" and >> "new" one. I try hard to avoid creating this situation, because the benefit is >> rarely worth the increased cognitive load. >> On 9/28/2022 1:57 PM, Brian Goetz wrote: >>> At various points, we've explored the question of which program elements are >>> most and least helpful for students first learning Java. After considering a >>> number of alternatives over the years, I have a simple proposal for smoothing >>> the "on ramp" to Java programming, while not creating new things to unlearn. >>> Markdown source is below, HTML will appear soon at: >>> [ https://openjdk.org/projects/amber/design-notes/on-ramp | >>> https://openjdk.org/projects/amber/design-notes/on-ramp ] >>> # Paving the on-ramp >>> Java is one of the most widely taught programming languages in the world. Tens >>> of thousands of educators find that the imperative core of the language combined >>> with a straightforward standard library is a foundation that students can >>> comfortably learn on. Choosing Java gives educators many degrees of freedom: >>> they can situate students in `jshell` or Notepad or a full-fledged IDE; they can >>> teach imperative, object-oriented, functional, or hybrid programming styles; and >>> they can easily find libraries to interact with external data and services. >>> No language is perfect, and one of the most common complaints about Java is that >>> it is "too verbose" or has "too much ceremony." And unfortunately, Java imposes >>> its heaviest ceremony on those first learning the language, who need and >>> appreciate it the least. The declaration of a class and the incantation of >>> `public static void main` is pure mystery to a beginning programmer. While >>> these incantations have principled origins and serve a useful organizing purpose >>> in larger programs, they have the effect of placing obstacles in the path of >>> _becoming_ Java programmers. Educators constantly remind us of the litany of >>> complexity that students have to confront on Day 1 of class -- when they really >>> just want to write their first program. >>> As an amusing demonstration of this, in her JavaOne keynote appearance in 2019, >>> [Aimee Lucido]( [ https://www.youtube.com/watch?v=BkPPFiXUwYk | >>> https://www.youtube.com/watch?v=BkPPFiXUwYk ] ) talked about when >>> she learned to program in Java, and how her teacher performed a rap song >>> to help students memorize `"public static void main"`. Our hats are off to >>> creative educators everywhere for this kind of dedication, but teachers >>> shouldn't have to do this. >>> Of course, advanced programmers complain about ceremony too. We will never be >>> able to satisfy programmers' insatiable appetite for typing fewer keystrokes, >>> and we shouldn't try, because the goal of programming is to write programs that >>> are easy to read and are clearly correct, not programs that were easy to type. >>> But we can try to better align the ceremony commensurate with the value it >>> brings to a program -- and let simple programs be expressed more simply. >>> ## Concept overload >>> The classic "Hello World" program looks like this in Java: >>> ``` >>> public class HelloWorld { >>> public static void main(String[] args) { >>> System.out.println("Hello World"); >>> } >>> } >>> ``` >>> It may only be five lines, but those lines are packed with concepts that are >>> challenging to absorb without already having some programming experience and >>> familiarity with object orientation. Let's break down the concepts a student >>> confronts when writing their first Java program: >>> - **public** (on the class). The `public` accessibility level is relevant >>> only when there is going to be cross-package access; in a simple "Hello >>> World" program, there is only one class, which lives in the unnamed package. >>> They haven't even written a one-line program yet; the notion of access >>> control -- keeping parts of a program from accessing other parts of it -- is >>> still way in their future. >>> - **class**. Our student hasn't set out to write a _class_, or model a >>> complex system with objects; they want to write a _program_. In Java, a >>> program is just a `main` method in some class, but at this point our student >>> still has no idea what a class is or why they want one. >>> - **Methods**. Methods are of course a key concept in Java, but the mechanics >>> of methods -- parameters, return types, and invocation -- are still >>> unfamiliar, and the `main` method is invoked magically from the `java` >>> launcher rather than from explicit code. >>> - **public** (again). Like the class, the `main` method has to be public, but >>> again this is only relevant when programs are large enough to require >>> packages to organize them. >>> - **static**. The `main` method has to be static, and at this point, students >>> have no context for understanding what a static method is or why they want >>> one. Worse, the early exposure to `static` methods will turn out to be a >>> bad habit that must be later unlearned. Worse still, the fact that the >>> `main` method is `static` creates a seam between `main` and other methods; >>> either they must become `static` too, or the `main` method must trampoline >>> to some sort of "instance main" (more ceremony!) And if we get this wrong, >>> we get the dreaded and mystifying `"cannot be referenced from a static >>> context"` error. >>> - **main**. The name `main` has special meaning in a Java program, indicating >>> the starting point of a program, but this specialness hides behind being an >>> ordinary method name. This may contribute to the sense of "so many magic >>> incantations." >>> - **String[]**. The parameter to `main` is an array of strings, which are the >>> arguments that the `java` launcher collected from the command line. But our >>> first program -- likely our first dozen -- will not use command-line >>> parameters. Requiring the `String[]` parameter is, at this point, a mistake >>> waiting to happen, and it will be a long time until this parameter makes >>> sense. Worse, educators may be tempted to explain arrays at this point, >>> which further increases the time-to-first-program. >>> - **System.out.println**. If you look closely at this incantation, each >>> element in the chain is a different thing -- `System` is a class (what's a >>> class again?), `out` is a static field (what's a field?), and `println` is >>> an instance method. The only part the student cares about right now is >>> `println`; the rest of it is an incantation that they do not yet understand >>> in order to get at the behavior they want. >>> That's a lot to explain to a student on the first day of class. There's a good >>> chance that by now, class is over and we haven't written any programs yet, or >>> the teacher has said "don't worry what this means, you'll understand it later" >>> six or eight times. Not only is this a lot of _syntactic_ things to absorb, but >>> each of those things appeals to a different concept (class, method, package, >>> return value, parameter, array, static, public, etc) that the student doesn't >>> have a framework for understanding yet. Each of these will have an important >>> role to play in larger programs, but so far, they only contribute to "wow, >>> programming is complicated." >>> It won't be practical (or even desirable) to get _all_ of these concepts out of >>> the student's face on day 1, but we can do a lot -- and focus on the ones that >>> do the most to help beginners understand how programs are constructed. >>> ## Goal: a smooth on-ramp >>> As much as programmers like to rant about ceremony, the real goal here is not >>> mere ceremony reduction, but providing a graceful _on ramp_ to Java programming. >>> This on-ramp should be helpful to beginning programmers by requiring only those >>> concepts that a simple program needs. >>> Not only should an on-ramp have a gradual slope and offer enough acceleration >>> distance to get onto the highway at the right speed, but its direction must >>> align with that of the highway. When a programmer is ready to learn about more >>> advanced concepts, they should not have to discard what they've already learned, >>> but instead easily see how the simple programs they've already written >>> generalize to more complicated ones, and both the syntatic and conceptual >>> transformation from "simple" to "full blown" program should be straightforward >>> and unintrusive. It is a definite non-goal to create a "simplified dialect of >>> Java for students". >>> We identify three simplifications that should aid both educators and students in >>> navigating the on-ramp to Java, as well as being generally useful to simple >>> programs beyond the classroom as well: >>> - A more tolerant launch protocol >>> - Unnamed classes >>> - Predefined static imports for the most critical methods and fields >>> ## A more tolerant launch protocol >>> The Java Language Specification has relatively little to say about how Java >>> "programs" get launched, other than saying that there is some way to indicate >>> which class is the initial class of a program (JLS 12.1.1) and that a public >>> static method called `main` whose sole argument is of type `String[]` and whose >>> return is `void` constitutes the entry point of the indicated class. >>> We can eliminate much of the concept overload simply by relaxing the >>> interactions between a Java program and the `java` launcher: >>> - Relax the requirement that the class, and `main` method, be public. Public >>> accessibility is only relevant when access crosses packages; simple programs >>> live in the unnamed package, so cannot be accessed from any other package >>> anyway. For a program whose main class is in the unnamed package, we can >>> drop the requirement that the class or its `main` method be public, >>> effectively treating the `java` launcher as if it too resided in the unnamed >>> package. >>> - Make the "args" parameter to `main` optional, by allowing the `java` launcher >>> to >>> first look for a main method with the traditional `main(String[])` >>> signature, and then (if not found) for a main method with no arguments. >>> - Make the `static` modifier on `main` optional, by allowing the `java` launcher >>> to >>> invoke an instance `main` method (of either signature) by instantiating an >>> instance using an accessible no-arg constructor and then invoking the `main` >>> method on it. >>> This small set of changes to the launch protocol strikes out five of the bullet >>> points in the above list of concepts: public (twice), static, method parameters, >>> and `String[]`. >>> At this point, our Hello World program is now: >>> ``` >>> class HelloWorld { >>> void main() { >>> System.out.println("Hello World"); >>> } >>> } >>> ``` >>> It's not any shorter by line count, but we've removed a lot of "horizontal >>> noise" along with a number of concepts. Students and educators will appreciate >>> it, but advanced programmers are unlikely to be in any hurry to make these >>> implicit elements explicit either. >>> Additionally, the notion of an "instance main" has value well beyond the first >>> day. Because excessive use of `static` is considered a code smell, many >>> educators encourage the pattern of "all the static `main` method does is >>> instantiate an instance and call an instance `main` method" anyway. Formalizing >>> the "instance main" protocol reduces a layer of boilerplate in these cases, and >>> defers the point at which we have to explain what instance creation is -- and >>> what `static` is. (Further, allowing the `main` method to be an instance method >>> means that it could be inherited from a superclass, which is useful for simple >>> frameworks such as test runners or service frameworks.) >>> ## Unnamed classes >>> In a simple program, the `class` declaration often doesn't help either, because >>> other classes (if there are any) are not going to reference it by name, and we >>> don't extend a superclass or implement any interfaces. If we say an "unnamed >>> class" consists of member declarations without a class header, then our Hello >>> World program becomes: >>> ``` >>> void main() { >>> System.out.println("Hello World"); >>> } >>> ``` >>> Such source files can still have fields, methods, and even nested classes, so >>> that as a program evolves from a few statements to needing some ancillary state >>> or helper methods, these can be factored out of the `main` method while still >>> not yet requiring a full class declaration: >>> ``` >>> String greeting() { return "Hello World"; } >>> void main() { >>> System.out.println(greeting()); >>> } >>> ``` >>> This is where treating `main` as an instance method really shines; the user has >>> just declared two methods, and they can freely call each other. Students need >>> not confront the confusing distinction between instance and static methods yet; >>> indeed, if not forced to confront static members on day 1, it might be a while >>> before they do have to learn this distinction. The fact that there is a >>> receiver lurking in the background will come in handy later, but right now is >>> not bothering anybody. >>> [JEP 330]( [ https://openjdk.org/jeps/330 | https://openjdk.org/jeps/330 ] ) >>> allows single-file programs to be >>> launched directly without compilation; this streamlined launcher pairs well with >>> unnamed classes. >>> ## Predefined static imports >>> The most important classes, such as `String` and `Integer`, live in the >>> `java.lang` package, which is automatically on-demand imported into all >>> compilation units; this is why we do not have to `import java.lang.String` in >>> every class. Static imports were not added until Java 5, but no corresponding >>> facility for automatic on-demand import of common behavior was added at that >>> time. Most programs, however, will want to do console IO, and Java forces us to >>> do this in a roundabout way -- through the static `System.out` and `System.in` >>> fields. Basic console input and output is a reasonable candidate for >>> auto-static import, as one or both are needed by most simple programs. While >>> these are currently instance methods accessed through static fields, we can >>> easily create static methods for `println` and `readln` which are suitable for >>> static import, and automatically import them. At which point our first program >>> is now down to: >>> ``` >>> void main() { >>> println("Hello World"); >>> } >>> ``` >>> ## Putting this all together >>> We've discussed several simplifications: >>> - Update the launcher protocol to make public, static, and arguments optional >>> for main methods, and for main methods to be instance methods (when a >>> no-argument constructor is available); >>> - Make the class wrapper for "main classes" optional (unnamed classes); >>> - Automatically static import methods like `println` >>> which together whittle our long list of day-1 concepts down considerably. While >>> this is still not as minimal as the minimal Python or Ruby program -- statements >>> must still live in a method -- the goal here is not to win at "code golf". The >>> goal is to ensure that concepts not needed by simple programs need not appear in >>> those programs, while at the same time not encouraging habits that have to be >>> unlearned as programs scale up. >>> Each of these simplifications is individually small and unintrusive, and each is >>> independent of the others. And each embodies a simple transformation that the >>> author can easily manually reverse when it makes sense to do so: elided >>> modifiers and `main` arguments can be added back, the class wrapper can be added >>> back when the affordances of classes are needed (supertypes, constructors), and >>> the full qualifier of static-import can be added back. And these reversals are >>> independent of one another; they can done in any combination or any order. >>> This seems to meet the requirements of our on-ramp; we've eliminated most of the >>> day-1 ceremony elements without introducing new concepts that need to be >>> unlearned. The remaining concepts -- a method is a container for statements, and >>> a program is a Java source file with a `main` method -- are easily understood in >>> relation to their fully specified counterparts. >>> ## Alternatives >>> Obviously, we've lived with the status quo for 25+ years, so we could continue >>> to do so. There were other alternatives explored as well; ultimately, each of >>> these fell afoul of one of our goals. >>> ### Can't we go further? >>> Fans of "code golf" -- of which there are many -- are surely right now trying to >>> figure out how to eliminate the last little bit, the `main` method, and allow >>> statements to exist at the top-level of a program. We deliberately stopped >>> short of this because it offers little value beyond the first few minutes, and >>> even that small value quickly becomes something that needs to be unlearned. >>> The fundamental problem behind allowing such "loose" statements is that >>> variables can be declared inside both classes (fields) and methods (local >>> variables), and they share the same syntactic production but not the same >>> semantics. So it is unclear (to both compilers and humans) whether a "loose" >>> variable would be a local or a field. If we tried to adopt some sort of simple >>> heuristic to collapse this ambiguity (e.g., whether it precedes or follows the >>> first statement), that may satisfy the compiler, but now simple refactorings >>> might subtly change the meaning of the program, and we'd be replacing the >>> explicit syntactic overhead of `void main()` with an invisible "line" in the >>> program that subtly affects semantics, and a new subtle rule about the meaning >>> of variable declarations that applies only to unnamed classes. This doesn't >>> help students, nor is this particularly helpful for all but the most trivial >>> programs. It quickly becomes a crutch to be discarded and unlearned, which >>> falls afoul of our "on ramp" goals. Of all the concepts on our list, "methods" >>> and "a program is specified by a main method" seem the ones that are most worth >>> asking students to learn early. >>> ### Why not "just" use `jshell`? >>> While JShell is a great interactive tool, leaning too heavily on it as an onramp >>> would fall afoul of our goals. A JShell session is not a program, but a >>> sequence of code snippets. When we type declarations into `jshell`, they are >>> viewed as implicitly static members of some unspecified class, with >>> accessibility is ignored completely, and statements execute in a context where >>> all previous declarations are in scope. This is convenient for experimentation >>> -- the primary goal of `jshell` -- but not such a great mental model for >>> learning to write Java programs. Transforming a batch of working declarations >>> in `jshell` to a real Java program would not be sufficiently simple or >>> unintrusive, and would lead to a non-idiomatic style of code, because the >>> straightforward translation would have us redeclaring each method, class, and >>> variable declaration as `static`. Further, this is probably not the direction >>> we want to go when we scale up from a handful of statements and declarations to >>> a simple class -- we probably want to start using classes as classes, not just >>> as containers for static members. JShell is a great tool for exploration and >>> debugging, and we expect many educators will continue to incorporate it into >>> their curriculum, but is not the on-ramp programming model we are looking for. >>> ### What about "always local"? >>> One of the main tensions that `main` introduces is that most class members are >>> not `static`, but the `main` method is -- and that forces programmers to >>> confront the seam between static and non-static members. JShell answers this >>> with "make everything static". >>> Another approach would be to "make everything local" -- treat a simple program >>> as being the "unwrapped" body of an implicit main method. We already allow >>> variables and classes to be declared local to a method. We could add local >>> methods (a useful feature in its own right) and relax some of the asymmetries >>> around nesting (again, an attractive cleanup), and then treat a mix of >>> declarations and statements without a class wrapper as the body of an invisible >>> `main` method. This seems an attractive model as well -- at first. >>> While the syntactic overhead of converting back to full-blown classes -- wrap >>> the whole thing in a `main` method and a `class` declaration -- is far less >>> intrusive than the transformation inherent in `jshell`, this is still not an >>> ideal on-ramp. Local variables interact with local classes (and methods, when >>> we have them) in a very different way than instance fields do with instance >>> methods and inner classes: their scopes are different (no forward references), >>> their initialization rules are different, and captured local variables must be >>> effectively final. This is a subtly different programming model that would then >>> have to be unlearned when scaling up to full classes. Further, the result of >>> this wrapping -- where everything is local to the main method -- is also not >>> "idiomatic Java". So while local methods may be an attractive feature, they are >>> similarly not the on-ramp we are looking for. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Oct 19 18:54:44 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 19 Oct 2022 14:54:44 -0400 Subject: Paving the on-ramp In-Reply-To: <276656784.29954930.1666201819779.JavaMail.zimbra@u-pem.fr> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <276656784.29954930.1666201819779.JavaMail.zimbra@u-pem.fr> Message-ID: > > We could, of course, prevent class access from outside the class > too, effectively making such classes visible only to the > launcher.? But this would come at no small cost to prevent such > accesses, and for limited benefit. > > > We can cheaply disallow access by > - only allowing one compilation unit if there is an unnamed class. > - mark the generated class as synthetic (see JVMS 4.7.8) so it does > not work with separate compilation. Yes, but this has several disadvantages. ?- More complicated translation schemes mean more speed bumps merging onto the highway; ?- It is a more limited programming model, users can't even use records unless they are local. > The status-quo proposal currently says: the name of the class is > not visible from within the class, you can't have constructors or > instance initializers, you get an implicit no-arg constructor like > every other constructor-less class.? If you want a more complex > construction protocol, or supertypes, or class declaration > annotations, or to live in a package, declare a class.? This still > seems pretty OK. > > > I still think that allowing fields inside an unamed class is a mistake > (if we can avoid to talk about null at the beginning, it's a win). I understand why this is appealing to you, but again, it smacks of trying to engineer a "beginner-safe programming model subset", which I think is a fool's game here.? The goal is to match the ceremony overhead to what the user actually uses.? Requiring a class declaration in order to declare a superclass makes sense -- because this is where a class declaration adds value -- but "no fields" seems more of a gratuitous, paternalistic restriction.? It also restricts the programming model in ways that rule out pedagogically useful intermediate refactorings, even if they are not a stable endpoint. -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Wed Oct 19 20:16:11 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 19 Oct 2022 13:16:11 -0700 Subject: Paving the on-ramp In-Reply-To: <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> Message-ID: On 19 Oct 2022, at 9:43, Brian Goetz wrote: > The alternative is to view these as _implicitly named_ classes, where > they have a name, just derived from the file system rather than the > source code. I?d like to discourage this idea. We already have nameless classes with non-denotable names, and programmers know how to use them. We don?t really have implicitly-named classes. (Except maybe in a weak sense for certain well-defined bytecode names like `pkg/Foo$Bar`, for member classes which already have an explicit name `Foo.Bar`; arguably `Foo$Bar` is an implicit name. But it cannot appear in source.) If we introduce a new way of naming (implicit names) we will have to roll out rules for mapping the name-precursor (a filename) to a name. This will have its own headaches, since different OSs have different alphabets and syntaxes, and none of those alphabets or syntaxes are fully compatible with native Java class names. So we?d have to saddle ourselves with a name mangling scheme to launder a random filename into a source-denotable Java class name. If ever there was a siren song, this is a loud one! Maybe the first place you?d want a name is a constructor declaration, not as an API point but as a place to put random setup code. Instance initialization blocks (though nobody loves them) supply a workaround for placing such random setup code. I suppose we could put some lipstick on them by allowing (eventually requiring) them to start with a keyword like `class` or `this`, in parallel to `static` for static initialization blocks. Or (different lipstick shade) allowing the init-blocks to somehow attach to field declarations, since that?s how they are used in many cases (both static and non-static). Since the next-best workaround is to give the class a name, or use a nested class to carry all the logic, and since that next-best workaround is not too expensive, I think the payoff for adding such lipstick is really small, but it?s something I?ve thought of before and might be worth keeping in the back pocket. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Oct 19 20:28:50 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 19 Oct 2022 22:28:50 +0200 (CEST) Subject: Paving the on-ramp In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> Message-ID: <1378342252.30024121.1666211330947.JavaMail.zimbra@u-pem.fr> > From: "John Rose" > To: "Brian Goetz" > Cc: "amber-spec-experts" > Sent: Wednesday, October 19, 2022 10:16:11 PM > Subject: Re: Paving the on-ramp > On 19 Oct 2022, at 9:43, Brian Goetz wrote: >> The alternative is to view these as _implicitly named_ classes, where they have >> a name, just derived from the file system rather than the source code. > I?d like to discourage this idea. We already have nameless classes with > non-denotable names, and programmers know how to use them. We don?t really have > implicitly-named classes. (Except maybe in a weak sense for certain > well-defined bytecode names like pkg/Foo$Bar , for member classes which already > have an explicit name Foo.Bar ; arguably Foo$Bar is an implicit name. But it > cannot appear in source.) It can appear in the source, '$' is a valid character for an identifier. > If we introduce a new way of naming (implicit names) we will have to roll out > rules for mapping the name-precursor (a filename) to a name. This will have its > own headaches, since different OSs have different alphabets and syntaxes, and > none of those alphabets or syntaxes are fully compatible with native Java class > names. So we?d have to saddle ourselves with a name mangling scheme to launder > a random filename into a source-denotable Java class name. If ever there was a > siren song, this is a loud one! yes, i would prefer a constant name like UnnamedClass. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Oct 19 20:35:46 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 19 Oct 2022 16:35:46 -0400 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> Message-ID: <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> Let's pull on this string. When we say `javac Foo.java`, the compiler has to create a class file, and doesn't have the benefit of a declared class name. The logical output file is `Foo.class`, because otherwise the next thing the user is likely to do is `java Foo`, and the class loader is going to look for Foo.class. A .class file has a ClassFile structure, which has a `this_class` field which names the current class.? We experimented with calling the class something like `$Foo` or $Unnamed, but this trick just garners a NoClassDefFoundError, with reason "wrong name".? This error comes from the native method `ClassLoader::defineClass1`. With inner classes, we've taken the position that class names with $ in their name are likely to be unstable names not to be counted on.? So calling it $Foo sends that signal, good.? But we'd have to be willing to loosen the checking in the class loader to allow loading a class with a slightly mangled name such as $Unnamed (and then make the launcher more tolerant of that.) On 10/19/2022 4:16 PM, John Rose wrote: > > On 19 Oct 2022, at 9:43, Brian Goetz wrote: > > The alternative is to view these as _implicitly named_ classes, > where they have a name, just derived from the file system rather > than the source code. > > I?d like to discourage this idea. We already have nameless classes > with non-denotable names, and programmers know how to use them. We > don?t really have implicitly-named classes. (Except maybe in a weak > sense for certain well-defined bytecode names like |pkg/Foo$Bar|, for > member classes which already have an explicit name |Foo.Bar|; arguably > |Foo$Bar| is an implicit name. But it cannot appear in source.) > > If we introduce a new way of naming (implicit names) we will have to > roll out rules for mapping the name-precursor (a filename) to a name. > This will have its own headaches, since different OSs have different > alphabets and syntaxes, and none of those alphabets or syntaxes are > fully compatible with native Java class names. So we?d have to saddle > ourselves with a name mangling scheme to launder a random filename > into a source-denotable Java class name. If ever there was a siren > song, this is a loud one! > > Maybe the first place you?d want a name is a constructor declaration, > not as an API point but as a place to put random setup code. Instance > initialization blocks (though nobody loves them) supply a workaround > for placing such random setup code. I suppose we could put some > lipstick on them by allowing (eventually requiring) them to start with > a keyword like |class| or |this|, in parallel to |static| for static > initialization blocks. Or (different lipstick shade) allowing the > init-blocks to somehow attach to field declarations, since that?s how > they are used in many cases (both static and non-static). > > Since the next-best workaround is to give the class a name, or use a > nested class to carry all the logic, and since that next-best > workaround is not too expensive, I think the payoff for adding such > lipstick is really small, but it?s something I?ve thought of before > and might be worth keeping in the back pocket. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Oct 19 21:00:22 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 19 Oct 2022 23:00:22 +0200 (CEST) Subject: Paving the on-ramp In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <276656784.29954930.1666201819779.JavaMail.zimbra@u-pem.fr> Message-ID: <32308342.30028882.1666213222282.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" > Cc: "amber-spec-experts" > Sent: Wednesday, October 19, 2022 8:54:44 PM > Subject: Re: Paving the on-ramp >>> We could, of course, prevent class access from outside the class too, >>> effectively making such classes visible only to the launcher. But this would >>> come at no small cost to prevent such accesses, and for limited benefit. >> We can cheaply disallow access by >> - only allowing one compilation unit if there is an unnamed class. >> - mark the generated class as synthetic (see JVMS 4.7.8) so it does not work >> with separate compilation. > Yes, but this has several disadvantages. > - More complicated translation schemes mean more speed bumps merging onto the > highway; > - It is a more limited programming model, users can't even use records unless > they are local. Users can still use record members too. But yes, everything has to be in the same compilation unit is perhaps too restrictive. Perhaps only marking the unnamed class as synthetic is a better tradeoff, it means you can compile it with other files but separate compilation is not supported. >>> The status-quo proposal currently says: the name of the class is not visible >>> from within the class, you can't have constructors or instance initializers, >>> you get an implicit no-arg constructor like every other constructor-less class. >>> If you want a more complex construction protocol, or supertypes, or class >>> declaration annotations, or to live in a package, declare a class. This still >>> seems pretty OK. >> I still think that allowing fields inside an unamed class is a mistake (if we >> can avoid to talk about null at the beginning, it's a win). > I understand why this is appealing to you, but again, it smacks of trying to > engineer a "beginner-safe programming model subset", which I think is a fool's > game here. The goal is to match the ceremony overhead to what the user actually > uses. Requiring a class declaration in order to declare a superclass makes > sense -- because this is where a class declaration adds value -- but "no > fields" seems more of a gratuitous, paternalistic restriction. It's about cognitive load, we are asking our users to internalize that code should be written only inside methods, but you are proposing that users will be able to write "int x;" outside a method. Most beginner to Java have been already exposed to either Python or JavaScript, so not being free to write code where they want is a main issue when starting. > It also restricts the programming model in ways that rule out pedagogically > useful intermediate refactorings, even if they are not a stable endpoint. What is the aim of those refactorings ? R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Wed Oct 19 22:59:34 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 19 Oct 2022 15:59:34 -0700 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> Message-ID: <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> On 19 Oct 2022, at 13:35, Brian Goetz wrote: > Let's pull on this string. > > When we say `javac Foo.java`, the compiler has to create a class file, > and doesn't have the benefit of a declared class name. The logical > output file is `Foo.class`, because otherwise the next thing the user > is likely to do is `java Foo`, and the class loader is going to look > for Foo.class. I guess what I?m suggesting here is that what?s new (or what *should* be new) is a full removal of all coupling between the file name (of the .class file) and the class?s bytecode name (in the ClassFile structure). VM anonymous classes (VMACs) are the precedent I?m looking at; IIRC only the package prefix of a VMAC is significant to any VM operation. (With minor exceptions: Reflection and stack traces probably report a string that depends on the bytecode name.) It seems reasonable to steer towards such a decoupling, because it would be (a) similar to what we have with VMACs, but with an accidental inclusion of a file system container *of an irrelevant name*; it would also be (b) useful to put zero constraints on the classfile name (other than a .class suffix perhaps) so that tools have fewer irrelevant details to worry about. > A .class file has a ClassFile structure, which has a `this_class` > field which names the current class. Names it for what purposes? For a nameless class, the only purposes are informative as with VMACs. I think that not even `Class.forName` should be able to recover the current class, only `this.getClass()`. Maybe that?s too much of a reach? > We experimented with calling the class something like `$Foo` or > $Unnamed, but this trick just garners a NoClassDefFoundError, with > reason "wrong name".? This error comes from the native method > `ClassLoader::defineClass1`. That?s a superficial problem. We are defining a new path for loading classes, so we have a right to adjust the rules. OTOH, and alternatively, and more in line with the gradual on-ramp: We could insist that, no, we are just defining an easier way to define a regular old class; we take care of issuing the name for you. Which is less surprising, that a named class you didn?t declare pops up somewhere and with a name vaguely like the filename, but is ?just a class you could have coded?, or that, if there is no name, none of the name-related operations work. I guess part of the issue is the anonymous classes (of various sorts) are two things at once: 1. a really obscure power-user feature, and 2. a desirable default early on the onramp. Having it both ways causes tension. If we embrace ?it is anonymous? I think we get the cleanest experience. People won?t be tempted to predict the name and refer to it somehow (with reflection or even source code). If we embrace ?it has a name you didn?t pick? I think we get a simpler onramp story, but at the cost of dark corners in the UE. Users will immediately try to exploit the fact that the name is semi-predictable, and write code that works with the names of these classes. > With inner classes, we've taken the position that class names with $ > in their name are likely to be unstable names not to be counted on.? > So calling it $Foo sends that signal, good.? But we'd have to be > willing to loosen the checking in the class loader to allow loading a > class with a slightly mangled name such as $Unnamed (and then make the > launcher more tolerant of that.) In the end, I?m totally willing to do this. Idea: Have the launcher not call defineClass at all, but rather take the byte image of the *.class file, and run it into the VM as a VMAC. That is a principled position that will prevent lots of nonsense about secret names. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Wed Oct 19 23:01:57 2022 From: john.r.rose at oracle.com (John Rose) Date: Wed, 19 Oct 2022 16:01:57 -0700 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> Message-ID: <45BDE171-C258-481E-8B52-0160500939AB@oracle.com> P.S. Immediately after pressing send I realized that my last suggestion only works for nameless classes that do not have nested class or interface members. Foo; getting those linked in brings back file-based names with a vengeance. Wish we had mclasses about now! From heidinga at redhat.com Thu Oct 20 12:45:02 2022 From: heidinga at redhat.com (Dan Heidinga) Date: Thu, 20 Oct 2022 08:45:02 -0400 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> Message-ID: On Wed, Oct 19, 2022 at 6:59 PM John Rose wrote: > On 19 Oct 2022, at 13:35, Brian Goetz wrote: > > Let's pull on this string. > > When we say `javac Foo.java`, the compiler has to create a class file, and > doesn't have the benefit of a declared class name. The logical output file > is `Foo.class`, because otherwise the next thing the user is likely to do > is `java Foo`, and the class loader is going to look for Foo.class. > > I guess what I?m suggesting here is that what?s new (or what *should* be > new) is a full removal of all coupling between the file name (of the .class > file) and the class?s bytecode name (in the ClassFile structure). VM > anonymous classes (VMACs) are the precedent I?m looking at; IIRC only the > package prefix of a VMAC is significant to any VM operation. (With minor > exceptions: Reflection and stack traces probably report a string that > depends on the bytecode name.) > > It seems reasonable to steer towards such a decoupling, because it would > be (a) similar to what we have with VMACs, but with an accidental inclusion > of a file system container *of an irrelevant name*; it would also be (b) > useful to put zero constraints on the classfile name (other than a .class > suffix perhaps) so that tools have fewer irrelevant details to worry about. > > A .class file has a ClassFile structure, which has a `this_class` field > which names the current class. > > Names it for what purposes? For a nameless class, the only purposes are > informative as with VMACs. I think that not even Class.forName should be > able to recover the current class, only this.getClass(). Maybe that?s too > much of a reach? > > We experimented with calling the class something like `$Foo` or $Unnamed, > but this trick just garners a NoClassDefFoundError, with reason "wrong > name". This error comes from the native method `ClassLoader::defineClass1`. > > That?s a superficial problem. We are defining a new path for loading > classes, so we have a right to adjust the rules. > > OTOH, and alternatively, and more in line with the gradual on-ramp: We > could insist that, no, we are just defining an easier way to define a > regular old class; we take care of issuing the name for you. Which is less > surprising, that a named class you didn?t declare pops up somewhere and > with a name vaguely like the filename, but is ?just a class you could have > coded?, or that, if there is no name, none of the name-related operations > work. > > I guess part of the issue is the anonymous classes (of various sorts) are > two things at once: 1. a really obscure power-user feature, and 2. a > desirable default early on the onramp. Having it both ways causes tension. > > If we embrace ?it is anonymous? I think we get the cleanest experience. > People won?t be tempted to predict the name and refer to it somehow (with > reflection or even source code). > > If we embrace ?it has a name you didn?t pick? I think we get a simpler > onramp story, but at the cost of dark corners in the UE. Users will > immediately try to exploit the fact that the name is semi-predictable, and > write code that works with the names of these classes. > After reading this (and the other thread), I'm more firmly convinced that using the file name is the right answer due to that "simpler onramp story". It allows the unnamed class to have a stable name which enables growing it to have proper constructors, referencing it from other classes, etc. Users trying to predict are learning more advanced features and will be ready to upgrade their classes to have explicit names. It becomes a much smaller step to then add the opening "class Foo {" & closing "}". If the goal is an easier on ramp, guiding them to understand the connection between Class names and file names (though not 100%) will help them navigate other source bases as well. This seems like a pure win. Treating it as anonymous doesn't help users to take that next step. --Dan > With inner classes, we've taken the position that class names with $ in > their name are likely to be unstable names not to be counted on. So > calling it $Foo sends that signal, good. But we'd have to be willing to > loosen the checking in the class loader to allow loading a class with a > slightly mangled name such as $Unnamed (and then make the launcher more > tolerant of that.) > > In the end, I?m totally willing to do this. > > Idea: Have the launcher not call defineClass at all, but rather take the > byte image of the *.class file, and run it into the VM as a VMAC. That is a > principled position that will prevent lots of nonsense about secret names. > > ? John > -------------- next part -------------- An HTML attachment was scrubbed... URL: From guy.steele at oracle.com Thu Oct 20 13:12:08 2022 From: guy.steele at oracle.com (Guy Steele) Date: Thu, 20 Oct 2022 13:12:08 +0000 Subject: Paving the on-ramp In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> Message-ID: <81473D6C-A105-4718-BBAE-61DF95923FAB@oracle.com> On Oct 19, 2022, at 4:16 PM, John Rose wrote: . . . If we introduce a new way of naming (implicit names) we will have to roll out rules for mapping the name-precursor (a filename) to a name. This will have its own headaches, since different OSs have different alphabets and syntaxes, and none of those alphabets or syntaxes are fully compatible with native Java class names. So we?d have to saddle ourselves with a name mangling scheme to launder a random filename into a source-denotable Java class name. If ever there was a siren song, this is a loud one! If we go down that path, this problem is easily addressed: simply reject the program if its file name is not trivially mappable (via the identity transformation) to a source-denotable Java class name. This rule may seem draconian to some, but it would serve the purpose of guiding the new programmer to use already customary file-naming conventions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Thu Oct 20 13:16:21 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 20 Oct 2022 13:16:21 +0000 Subject: Paving the on-ramp In-Reply-To: <81473D6C-A105-4718-BBAE-61DF95923FAB@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <81473D6C-A105-4718-BBAE-61DF95923FAB@oracle.com> Message-ID: <2405BCFB-E3E4-4318-B522-7B219F255976@oracle.com> The POC indeed does this now (error otherwise). On Oct 20, 2022, at 10:12 AM, Guy Steele > wrote: On Oct 19, 2022, at 4:16 PM, John Rose > wrote: . . . If we introduce a new way of naming (implicit names) we will have to roll out rules for mapping the name-precursor (a filename) to a name. This will have its own headaches, since different OSs have different alphabets and syntaxes, and none of those alphabets or syntaxes are fully compatible with native Java class names. So we?d have to saddle ourselves with a name mangling scheme to launder a random filename into a source-denotable Java class name. If ever there was a siren song, this is a loud one! If we go down that path, this problem is easily addressed: simply reject the program if its file name is not trivially mappable (via the identity transformation) to a source-denotable Java class name. This rule may seem draconian to some, but it would serve the purpose of guiding the new programmer to use already customary file-naming conventions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Thu Oct 20 13:20:35 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 20 Oct 2022 13:20:35 +0000 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> Message-ID: <470EB0CD-D62F-49AE-973E-DD2A1DB9EB70@oracle.com> I like this progression; unnamed -> named . (Also named -> unnamed.) On Oct 20, 2022, at 9:45 AM, Dan Heidinga > wrote: On Wed, Oct 19, 2022 at 6:59 PM John Rose > wrote: On 19 Oct 2022, at 13:35, Brian Goetz wrote: Let's pull on this string. When we say `javac Foo.java`, the compiler has to create a class file, and doesn't have the benefit of a declared class name. The logical output file is `Foo.class`, because otherwise the next thing the user is likely to do is `java Foo`, and the class loader is going to look for Foo.class. I guess what I?m suggesting here is that what?s new (or what should be new) is a full removal of all coupling between the file name (of the .class file) and the class?s bytecode name (in the ClassFile structure). VM anonymous classes (VMACs) are the precedent I?m looking at; IIRC only the package prefix of a VMAC is significant to any VM operation. (With minor exceptions: Reflection and stack traces probably report a string that depends on the bytecode name.) It seems reasonable to steer towards such a decoupling, because it would be (a) similar to what we have with VMACs, but with an accidental inclusion of a file system container of an irrelevant name; it would also be (b) useful to put zero constraints on the classfile name (other than a .class suffix perhaps) so that tools have fewer irrelevant details to worry about. A .class file has a ClassFile structure, which has a `this_class` field which names the current class. Names it for what purposes? For a nameless class, the only purposes are informative as with VMACs. I think that not even Class.forName should be able to recover the current class, only this.getClass(). Maybe that?s too much of a reach? We experimented with calling the class something like `$Foo` or $Unnamed, but this trick just garners a NoClassDefFoundError, with reason "wrong name". This error comes from the native method `ClassLoader::defineClass1`. That?s a superficial problem. We are defining a new path for loading classes, so we have a right to adjust the rules. OTOH, and alternatively, and more in line with the gradual on-ramp: We could insist that, no, we are just defining an easier way to define a regular old class; we take care of issuing the name for you. Which is less surprising, that a named class you didn?t declare pops up somewhere and with a name vaguely like the filename, but is ?just a class you could have coded?, or that, if there is no name, none of the name-related operations work. I guess part of the issue is the anonymous classes (of various sorts) are two things at once: 1. a really obscure power-user feature, and 2. a desirable default early on the onramp. Having it both ways causes tension. If we embrace ?it is anonymous? I think we get the cleanest experience. People won?t be tempted to predict the name and refer to it somehow (with reflection or even source code). If we embrace ?it has a name you didn?t pick? I think we get a simpler onramp story, but at the cost of dark corners in the UE. Users will immediately try to exploit the fact that the name is semi-predictable, and write code that works with the names of these classes. After reading this (and the other thread), I'm more firmly convinced that using the file name is the right answer due to that "simpler onramp story". It allows the unnamed class to have a stable name which enables growing it to have proper constructors, referencing it from other classes, etc. Users trying to predict are learning more advanced features and will be ready to upgrade their classes to have explicit names. It becomes a much smaller step to then add the opening "class Foo {" & closing "}". If the goal is an easier on ramp, guiding them to understand the connection between Class names and file names (though not 100%) will help them navigate other source bases as well. This seems like a pure win. Treating it as anonymous doesn't help users to take that next step. --Dan With inner classes, we've taken the position that class names with $ in their name are likely to be unstable names not to be counted on. So calling it $Foo sends that signal, good. But we'd have to be willing to loosen the checking in the class loader to allow loading a class with a slightly mangled name such as $Unnamed (and then make the launcher more tolerant of that.) In the end, I?m totally willing to do this. Idea: Have the launcher not call defineClass at all, but rather take the byte image of the *.class file, and run it into the VM as a VMAC. That is a principled position that will prevent lots of nonsense about secret names. ? John -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Oct 20 16:12:56 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 20 Oct 2022 12:12:56 -0400 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> Message-ID: > If we embrace ?it is anonymous? I think we get the cleanest > experience. People won?t be tempted to predict the name and refer > to it somehow (with reflection or even source code). > > If we embrace ?it has a name you didn?t pick? I think we get a > simpler onramp story, but at the cost of dark corners in the UE. > Users will immediately try to exploit the fact that the name is > semi-predictable, and write code that works with the names of > these classes. > > After reading this (and the other thread), I'm more firmly convinced > that using the file name is the right answer due to that "simpler > onramp story".? It allows the unnamed class to have a stable name > which enables growing it to have proper constructors, referencing it > from other classes, etc.? Users trying to predict are learning more > advanced features and will be ready to upgrade their classes to have > explicit names.? It becomes a much smaller step to then add the > opening "class Foo {"? & closing "}". This is a pretty compelling argument.? You start out with what appears to be a "naked" method, you add some more methods, maybe variables (sorry Remi) and helper classes, and then at some point, the teacher explains "class".? And then says, "hey, you didn't realize it, but you already wrote some classes!"? (Cue "you're soaking in it" commercial clip from the 70s.) There's a seed of this argument already in the initial writeup, where the `this` receiver has been lurking in the background, and we mostly didn't notice or ignored it.? Its there in all the methods, we had no reason to hide it, we are just not shining the spotlight on it.? Dan, you're saying "exact same thing for the class name". But, I hear John say, this implicit class sucks, it has a name derived from some arbitrary artifact, maybe its name is FOO because the file system isn't case sensitive, etc.? OK, well, if you need to use the name, and you don't like the implicit one, then .... give it a name.? You've just learned why we give classes names. So having explored the alternatives a bit farther, I'm getting more comfortable with "accidental name". -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 3 23:38:32 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 03 Oct 2022 23:38:32 -0000 Subject: Draft JEPs: Pattern Matching for switch and Record Patterns In-Reply-To: <277f5d2c-d36a-e791-48d8-cd35e10bc8f7@oracle.com> References: <6F760336-DE5D-45C0-A37A-680E84E81EF0@oracle.com> <304331103.17970907.1664814348728.JavaMail.zimbra@u-pem.fr> <277f5d2c-d36a-e791-48d8-cd35e10bc8f7@oracle.com> Message-ID: <1473616214.18068711.1664840301143.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" , "Gavin Bierman" > Cc: "Tagir Valeev" , "amber-dev" , > "amber-spec-experts" > Sent: Tuesday, October 4, 2022 12:16:21 AM > Subject: Re: Draft JEPs: Pattern Matching for switch and Record Patterns > I was skeptical at first too, but the weight of the points Gavin raises makes me > feel that this aspect was (a) a little rushed and (b) not critical, so backing > it out now gives us a chance to think it through further, and bring it back > later in this or another form. I agree on (a), we need to put more work on it. Nothing here is critical so i suppose i agree on (b) but i would prefer to keep it because i want people to test mixing any patterns ('_') and record patterns, e.g. case Point p -> ... vs case Point(_,_) p -> ... vs case Point(int _, int _) p -> ... R?mi > On 10/3/2022 12:25 PM, Remi Forax wrote: >>> From: "Gavin Bierman" [ mailto:gavin.bierman at oracle.com | >>> ] >>> To: "Tagir Valeev" [ mailto:amaembo at gmail.com | ] >>> Cc: "amber-dev" [ mailto:amber-dev at openjdk.org | ] , >>> "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.org | >>> ] >>> Sent: Monday, October 3, 2022 5:29:40 PM >>> Subject: Re: Draft JEPs: Pattern Matching for switch and Record Patterns >>> Hi Tagir, >> The main objection to remove the name of the record pattern is that it does not >> follow the principle of the data oriented programming. >> The idea is that the data is more important than the code, or said differently, >> if the data change by example a component is added to a record, the compiler >> should flag all the code that uses that record and ask the user to modify the >> code. >> So a case with a record pattern is better than just a type pattern, because >> unlike a type pattern, a record pattern validates the shape of a record >> case Point p // does not validate the shape of Point >> case Point(int x, int y) p // validates that a Point has two components x and y. >> When using virtual polymorphism, an operation is defined as an abstract method, >> so if the record shape changes, people will scan the rest of the record and >> change the implementation of the methods according to the new components. If >> the operation uses pattern matching, the record and the operation are not >> declared at the same place, so the compiler has to help users to find all the >> locations in the code that should be updated. >>> So it was a number of issues actually. First, there is a nasty ambiguity >>> problem. Consider: >>> record R(){} >>> switch(e) { >>> case R() when when (true) -> ... >>> ... >>> } >>> The label could be parsed as either: >>> case (R() when) when (true) -> >>> or >>> case (R()) when (when(true)) -> >>> (where ` when ` is a static boolean method). >> It's a usual issue with local keywords, we had the same kind of issue with the >> local keywords inside modules (transitive as a keyword or has a package name). >> A solution on top of my head is to make "when" a keyword for the whole case >> (everything in between "case" and "->"), so having to consecutive "when" is not >> syntactically valid. >> It's not the only option, and i don't think it's a showstopper. >>> There is another issue which is this variable declaration is the only one in the >>> language that can?t be annotated or marked as `final` which feels like a design >>> smell. None of the obvious solutions to this looked good. >> For me, a group pattern is not really different than a type pattern for this >> concern, >> the current syntax is with a type pattern is >> case final Point p -> >> so the syntax for a record pattern is >> case final Point(int x,int y) p -> >> It awkward and super verbose but it's a straight consequence of not having the >> binding always final. >>> In most other languages with pattern matching they keep these two things - a >>> destructing pattern and a naming pattern - separate. In both Haskell and Scala, >>> they write `x at p` to ?name? a pattern p as x. So that seems like a possibility. >>> But for now, we noted that in most cases you can rewrite pretty simply, e.g. >>> case Point(var x, var y) when p.isVisible() -> >>> can be rewritten: >>> case Point p >>> when p.isVisible() && p instanceof Point(var x, var y): ? >>> or if it was in an instanceof: >>> if (x instanceof Point p && p.isVisible() && p instanceof Point(var x, var y)) { >>> ? } >>> Neither of these versions read too badly. >> I disagree, a case ... does not exist in the vacuum but works and is read with >> the other cases. >> Here, following "case Point p when ... ", you will have a proper record pattern >> of Point to be exhaustive and the lack of common prefix between the two >> patterns makes the code hard to read. >> Point p = ... >> switch(p) { >> case Point p >> when p.isVisible() && p instanceof Point (var x, var y) -> ... >> case Point(int x, int y) -> ... >> } >> compared to >> Point p = ... >> switch(p) { >> case Point(int x, int y) p when p.isVisible() -> ... >> case Point(int x, int y) p -> ... >> } >> Here, it's clear that the first line is a peculiar case of the second line. >>> Thoughts? >>> Gavin >> R?mi >>>> On 3 Oct 2022, at 14:40, Tagir Valeev < [ mailto:amaembo at gmail.com | >>>> amaembo at gmail.com ] > wrote: >>>> Hello! >>>>> Remove support for named record patterns. >>>> This surprises me. Probably there was a discussion about the rationale >>>> behind this change? Could you please point me? Thanks. >>>> With best regards, >>>> Tagir Valeev >>>> On Mon, Oct 3, 2022 at 2:48 PM Gavin Bierman < [ mailto:gavin.bierman at oracle.com >>>> | gavin.bierman at oracle.com ] > wrote: >>>>> Dear all, >>>>> The draft JEPs for the next preview of the Pattern Matching for switch and >>>>> Record Patterns features are now available at: >>>>> Pattern matching for switch: [ https://bugs.openjdk.org/browse/JDK-8294285 | >>>>> https://bugs.openjdk.org/browse/JDK-8294285 ] >>>>> Record Patterns: [ https://bugs.openjdk.org/browse/JDK-8294078 | >>>>> https://bugs.openjdk.org/browse/JDK-8294078 ] >>>>> Comments welcomed! >>>>> Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Thu Oct 20 14:44:07 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 20 Oct 2022 16:44:07 +0200 Subject: [string-templates] Accidental use of StringTemplate instead of string Message-ID: Hello! As stated in JEP 430 text, the string template is a well-formed expression of StringTemplate type. Also, toString() method of StringTemplate prints a debug-friendly representation like [fragments](values) which is not intended for actual use. I can imagine that the following mistake may happen often, especially by inexperienced developers, or developers with background in other languages: int x = 10, y = 20; System.out.println("\{x} plus \{y} equals \{x + y}"); Output expected by programmer: 10 plus 20 equals 30 Actual output (successfully compilable): ["", " plus ", " equals ", ""](10, 20, 30) As an IDE vendor, we can surely provide an inspection for some well-known methods like PrintStream#println(Object) with a quick-fix to add a STR. prefix. However, not everybody uses IDEs. Probably the language itself could help here somehow? E.g., uses of StringTemplate where the Object type is expected might trigger a warning. This is similar to array toString(), and every static analyzer has an inspection about this. Honestly, I don't know a good solution but I feel that it's not a good idea to introduce a new error-prone construct, which is compilable and runnable, could be mistakenly used and doesn't make sense. As we are here, is it planned to specify StringTemplate equals and hashCode? It's naturally a tuple of (fragments, values) lists, so equals and hashCode could be defined. Also, current draft [1] suggests that this is a non-sealed interface. Is it really intended to allow third-party implementations? Sorry if these questions were already discussed, I might skip some discussions. I would be happy to see the pointers to previous discussions. With best regards, Tagir Valeev [1] http://cr.openjdk.java.net/~jlaskey/templates/docs/api/java.base/java/lang/template/StringTemplate.html From james.laskey at oracle.com Thu Oct 20 16:15:10 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 20 Oct 2022 16:15:10 +0000 Subject: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: Message-ID: Tagir, > On Oct 20, 2022, at 11:44 AM, Tagir Valeev wrote: > > Hello! > > As stated in JEP 430 text, the string template is a well-formed > expression of StringTemplate type. Also, toString() method of > StringTemplate prints a debug-friendly representation like > [fragments](values) which is not intended for actual use. I can > imagine that the following mistake may happen often, especially by > inexperienced developers, or developers with background in other > languages: > > int x = 10, y = 20; > System.out.println("\{x} plus \{y} equals \{x + y}"); > > Output expected by programmer: 10 plus 20 equals 30 > Actual output (successfully compilable): ["", " plus ", " equals ", > ""](10, 20, 30) > > As an IDE vendor, we can surely provide an inspection for some > well-known methods like PrintStream#println(Object) with a quick-fix > to add a STR. prefix. However, not everybody uses IDEs. Probably the > language itself could help here somehow? E.g., uses of StringTemplate > where the Object type is expected might trigger a warning. This is > similar to array toString(), and every static analyzer has an > inspection about this. Honestly, I don't know a good solution but I > feel that it's not a good idea to introduce a new error-prone > construct, which is compilable and runnable, could be mistakenly used > and doesn't make sense. There was some discussion early on about overloading print/println/format (StringBuilder::append) with an StringTemplate argument that would internally do the (optimal) interpolation. If I remember correctly, the discussion digressed into what was "least surprise?. The problem here is we can?t have it both ways. If we treat it ?like a string? in these cases then user will expect it to be a ?like a string? everywhere. Another option would have been to make StringTemplate::toString generate UnsupportedOperationException, which would cause havoc for debuggers. As always, we are open to suggestions, but I think this is one of those cases where users ?learn? to do the right thing. BTW: I?ll add ?StringTemplate? to the front of the toString. > > As we are here, is it planned to specify StringTemplate equals and > hashCode? It's naturally a tuple of (fragments, values) lists, so > equals and hashCode could be defined. This is something I slipped up on and has been mentioned by others. I will add. > Also, current draft [1] suggests > that this is a non-sealed interface. Is it really intended to allow > third-party implementations? Two reasons for this, primary is that the compiler generates non-uniform classes to represent StringTemplates. Secondarily there is an opportunity for other languages to map templates from their language. Also, if a user wants to implement promise-like templates read from files or use ${} syntax in strings with values come from maps or enumerations, then they are free to do so. > > Sorry if these questions were already discussed, I might skip some > discussions. I would be happy to see the pointers to previous > discussions. Comments are always welcome. There has been little discussion here, but here is the place. BTW: I can discuss parsing patterns when you are ready. > > With best regards, > Tagir Valeev > > [1] http://cr.openjdk.java.net/~jlaskey/templates/docs/api/java.base/java/lang/template/StringTemplate.html Cheers, ? Jim From brian.goetz at oracle.com Thu Oct 20 16:32:00 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 20 Oct 2022 12:32:00 -0400 Subject: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: Message-ID: <8d98c7f5-f65e-bf34-aa15-8a60fa5d0c69@oracle.com> The goal here is to help users learn, through feedback, that a StringTemplate is not a string, but a recipe for one, and if you want to get that string, you have to cook it. One way to help that along might be to have the toString look more like that of a record: ??? StringTemplate{ segments = [ blah blah ], values = [ blah blah ] } which will reinforce that you printed out a recipe and not the thing it describes. On 10/20/2022 10:44 AM, Tagir Valeev wrote: > Hello! > > As stated in JEP 430 text, the string template is a well-formed > expression of StringTemplate type. Also, toString() method of > StringTemplate prints a debug-friendly representation like > [fragments](values) which is not intended for actual use. I can > imagine that the following mistake may happen often, especially by > inexperienced developers, or developers with background in other > languages: > > int x = 10, y = 20; > System.out.println("\{x} plus \{y} equals \{x + y}"); > > Output expected by programmer: 10 plus 20 equals 30 > Actual output (successfully compilable): ["", " plus ", " equals ", > ""](10, 20, 30) > > As an IDE vendor, we can surely provide an inspection for some > well-known methods like PrintStream#println(Object) with a quick-fix > to add a STR. prefix. However, not everybody uses IDEs. Probably the > language itself could help here somehow? E.g., uses of StringTemplate > where the Object type is expected might trigger a warning. This is > similar to array toString(), and every static analyzer has an > inspection about this. Honestly, I don't know a good solution but I > feel that it's not a good idea to introduce a new error-prone > construct, which is compilable and runnable, could be mistakenly used > and doesn't make sense. > > As we are here, is it planned to specify StringTemplate equals and > hashCode? It's naturally a tuple of (fragments, values) lists, so > equals and hashCode could be defined. Also, current draft [1] suggests > that this is a non-sealed interface. Is it really intended to allow > third-party implementations? > > Sorry if these questions were already discussed, I might skip some > discussions. I would be happy to see the pointers to previous > discussions. > > With best regards, > Tagir Valeev > > [1]http://cr.openjdk.java.net/~jlaskey/templates/docs/api/java.base/java/lang/template/StringTemplate.html -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Thu Oct 20 16:48:34 2022 From: john.r.rose at oracle.com (John Rose) Date: Thu, 20 Oct 2022 09:48:34 -0700 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> Message-ID: <754C601A-2E2F-4A03-B59C-2D054AF7D487@oracle.com> On 20 Oct 2022, at 9:12, Brian Goetz wrote: >> If we embrace ?it is anonymous? I think we get the cleanest >> experience. People won?t be tempted to predict the name and refer >> to it somehow (with reflection or even source code). >> >> If we embrace ?it has a name you didn?t pick? I think we get a >> simpler onramp story, but at the cost of dark corners in the UE. >> Users will immediately try to exploit the fact that the name is >> semi-predictable, and write code that works with the names of >> these classes. >> >> After reading this (and the other thread), I'm more firmly convinced that using the file name is the right answer due to that "simpler onramp story".? It allows the unnamed class to have a stable name which enables growing it to have proper constructors, referencing it from other classes, etc.? Users trying to predict are learning more advanced features and will be ready to upgrade their classes to have explicit names.? It becomes a much smaller step to then add the opening "class Foo {"? & closing "}". > > This is a pretty compelling argument.? You start out with what appears to be a "naked" method, you add some more methods, maybe variables (sorry Remi) and helper classes, and then at some point, the teacher explains "class".? And then says, "hey, you didn't realize it, but you already wrote some classes!"? (Cue "you're soaking in it" commercial clip from the 70s.) > > There's a seed of this argument already in the initial writeup, where the `this` receiver has been lurking in the background, and we mostly didn't notice or ignored it.? Its there in all the methods, we had no reason to hide it, we are just not shining the spotlight on it.? Dan, you're saying "exact same thing for the class name". > > But, I hear John say, this implicit class sucks, it has a name derived from some arbitrary artifact, maybe its name is FOO because the file system isn't case sensitive, etc.? OK, well, if you need to use the name, and you don't like the implicit one, then .... give it a name.? You've just learned why we give classes names. > > So having explored the alternatives a bit farther, I'm getting more comfortable with "accidental name". Yeah, I?m getting there too after pulling on a string I liked more at first than I do now. Thanks Dan. Next question: If we are embracing accidental names, what are the rules for the name-precursors? Can we extract an accidental name from any possible file name in any possible file system? Or are there restrictions on the file names? Since this is a pedagogical feature, I suppose we require the ?.java? suffix always, despite the inevitable requests for shebang script support. And in particular, do we require that the basename of the file be a valid Java identifier (and not a Java keyword)? So no ?my-stuff.java? or ?42.java? or ?goto.java?? Do you have to learn Java identifier syntax in the very first place? (Seems reasonable to me at this moment.) From john.r.rose at oracle.com Thu Oct 20 16:51:38 2022 From: john.r.rose at oracle.com (John Rose) Date: Thu, 20 Oct 2022 09:51:38 -0700 Subject: Paving the on-ramp In-Reply-To: <2405BCFB-E3E4-4318-B522-7B219F255976@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <81473D6C-A105-4718-BBAE-61DF95923FAB@oracle.com> <2405BCFB-E3E4-4318-B522-7B219F255976@oracle.com> Message-ID: <5D005675-2778-4AD4-AA97-4E3DF252EF62@oracle.com> Yep. ?Lesson Zero: Java identifiers. Lesson One: Running a text editor.? A little odd, but the purpose is learning Java. On 20 Oct 2022, at 6:16, Jim Laskey wrote: > The POC indeed does this now (error otherwise). > > > >> On Oct 20, 2022, at 10:12 AM, Guy Steele >> wrote: > >> >>> On Oct 19, 2022, at 4:16 PM, John Rose >>> wrote: . . . >> >>> If we introduce a new way of naming (implicit names) we will have to >>> roll out rules for mapping the name-precursor (a filename) to a >>> name. This will have its own headaches, since different OSs have >>> different alphabets and syntaxes, and none of those alphabets or >>> syntaxes are fully compatible with native Java class names. So >>> we?d have to saddle ourselves with a name mangling scheme to >>> launder a random filename into a source-denotable Java class name. >>> If ever there was a siren song, this is a loud one! >> >> If we go down that path, this problem is easily addressed: simply >> reject the program if its file name is not trivially mappable (via >> the identity transformation) to a source-denotable Java class name. >> This rule may seem draconian to some, but it would serve the purpose >> of guiding the new programmer to use already customary file-naming >> conventions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Thu Oct 20 16:58:48 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 20 Oct 2022 16:58:48 +0000 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: <754C601A-2E2F-4A03-B59C-2D054AF7D487@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> <754C601A-2E2F-4A03-B59C-2D054AF7D487@oracle.com> Message-ID: Name is checked as a valid JavaIdentifier. > On Oct 20, 2022, at 1:48 PM, John Rose wrote: > > On 20 Oct 2022, at 9:12, Brian Goetz wrote: > >>> If we embrace ?it is anonymous? I think we get the cleanest >>> experience. People won?t be tempted to predict the name and refer >>> to it somehow (with reflection or even source code). >>> >>> If we embrace ?it has a name you didn?t pick? I think we get a >>> simpler onramp story, but at the cost of dark corners in the UE. >>> Users will immediately try to exploit the fact that the name is >>> semi-predictable, and write code that works with the names of >>> these classes. >>> >>> After reading this (and the other thread), I'm more firmly convinced that using the file name is the right answer due to that "simpler onramp story". It allows the unnamed class to have a stable name which enables growing it to have proper constructors, referencing it from other classes, etc. Users trying to predict are learning more advanced features and will be ready to upgrade their classes to have explicit names. It becomes a much smaller step to then add the opening "class Foo {" & closing "}". >> >> This is a pretty compelling argument. You start out with what appears to be a "naked" method, you add some more methods, maybe variables (sorry Remi) and helper classes, and then at some point, the teacher explains "class". And then says, "hey, you didn't realize it, but you already wrote some classes!" (Cue "you're soaking in it" commercial clip from the 70s.) >> >> There's a seed of this argument already in the initial writeup, where the `this` receiver has been lurking in the background, and we mostly didn't notice or ignored it. Its there in all the methods, we had no reason to hide it, we are just not shining the spotlight on it. Dan, you're saying "exact same thing for the class name". >> >> But, I hear John say, this implicit class sucks, it has a name derived from some arbitrary artifact, maybe its name is FOO because the file system isn't case sensitive, etc. OK, well, if you need to use the name, and you don't like the implicit one, then .... give it a name. You've just learned why we give classes names. >> >> So having explored the alternatives a bit farther, I'm getting more comfortable with "accidental name". > > Yeah, I?m getting there too after pulling on a string I liked more at first than I do now. Thanks Dan. > > Next question: If we are embracing accidental names, what are the rules for the name-precursors? Can we extract an accidental name from any possible file name in any possible file system? Or are there restrictions on the file names? Since this is a pedagogical feature, I suppose we require the ?.java? suffix always, despite the inevitable requests for shebang script support. > > And in particular, do we require that the basename of the file be a valid Java identifier (and not a Java keyword)? So no ?my-stuff.java? or ?42.java? or ?goto.java?? Do you have to learn Java identifier syntax in the very first place? (Seems reasonable to me at this moment.) From brian.goetz at oracle.com Thu Oct 20 17:04:03 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 20 Oct 2022 13:04:03 -0400 Subject: Unnamed classes (was: Paving the on-ramp) In-Reply-To: <754C601A-2E2F-4A03-B59C-2D054AF7D487@oracle.com> References: <1b6200d3-a7a6-6479-e8ab-d932eedbceb1@oracle.com> <10efb26a-649a-a258-a0ec-835f7453d025@oracle.com> <413ded65-2724-6438-9181-1d2c2362ddda@oracle.com> <13BE65D7-BEDD-4660-8F6B-742FFEC9656A@oracle.com> <754C601A-2E2F-4A03-B59C-2D054AF7D487@oracle.com> Message-ID: <7ab6a430-ece4-4889-1f6f-6e18b205190e@oracle.com> > Yeah, I?m getting there too after pulling on a string I liked more at first than I do now. Thanks Dan. > > Next question: If we are embracing accidental names, what are the rules for the name-precursors? Can we extract an accidental name from any possible file name in any possible file system? Or are there restrictions on the file names? Since this is a pedagogical feature, I suppose we require the ?.java? suffix always, despite the inevitable requests for shebang script support. A class name must conform to various constraints, such as matching the "Identifier"production; the compiler should demand the same of the implicit name. The compiler currently enforces matching of the class name to the file name (for public classes); we can enforce the same on the (implicit name, file name) pair. These mean that if you want to add the `class Foo { .. }` wrapper, you won't run into accidental problems.? (I spy an embedding-projection pair; stay within the embeddable domain.) From asviraspossible at gmail.com Fri Oct 21 13:05:07 2022 From: asviraspossible at gmail.com (Victor Nazarov) Date: Fri, 21 Oct 2022 15:05:07 +0200 Subject: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: Message-ID: Hello! I think one suggestion that fixes stated problems was already proposed before and I heard no arguments against going this route. On Thu, Oct 20, 2022 at 6:30 PM Jim Laskey wrote: > Tagir, > > > On Oct 20, 2022, at 11:44 AM, Tagir Valeev wrote: > > > > Hello! > > > > As stated in JEP 430 text, the string template is a well-formed > > expression of StringTemplate type. Also, toString() method of > > StringTemplate prints a debug-friendly representation like > > [fragments](values) which is not intended for actual use. I can > > imagine that the following mistake may happen often, especially by > > inexperienced developers, or developers with background in other > > languages: > > > > int x = 10, y = 20; > > System.out.println("\{x} plus \{y} equals \{x + y}"); > > > > Output expected by programmer: 10 plus 20 equals 30 > > Actual output (successfully compilable): ["", " plus ", " equals ", > > ""](10, 20, 30) > > > > As an IDE vendor, we can surely provide an inspection for some > > well-known methods like PrintStream#println(Object) with a quick-fix > > to add a STR. prefix. However, not everybody uses IDEs. Probably the > > language itself could help here somehow? > > > > > The problem here is we can?t have it both ways. If we treat it ?like a > string? in these cases then user will expect it to be a ?like a string? > everywhere. > > Another option would have been to make StringTemplate::toString generate > UnsupportedOperationException, which would cause havoc for debuggers. > > As always, we are open to suggestions, but I think this is one of those > cases where users ?learn? to do the right thing. > > One suggestion that fixes this problem is to forbid bare template strings without policy-prefixes and instead introduce some policy that returns TemplateString itself. ```` StringTemplate tmpl = TMPL."\{x} plus \{y} equals \{x + y}" ; String s1 = tmpl.apply(STR); String s2 = STR."\{x} plus \{y} equals \{x + y}"; assertEquals(s1, s2); ```` In this world the following can be observed: ```` System.out.println(STR."\{x} plus \{y} equals \{x + y}"); // Prints "10 plus 20 equals 30" System.out.println(TMPL."\{x} plus \{y} equals \{x + y}"); // Prints "StringTemplate{segments = ["", " plus ", " equals "], values = [10, 20, 30]}" System.out.println("\{x} plus \{y} equals \{x + y}"); // COMPILATION ERROR: error: unrecognized escape sequence '\{', policy is required for template strings ```` -- Victor Nazarov -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Fri Oct 21 20:34:24 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Fri, 21 Oct 2022 22:34:24 +0200 Subject: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: Message-ID: Hello! On Fri, Oct 21, 2022, 15:05 Victor Nazarov wrote: > Hello! > > I think one suggestion that fixes stated problems was already proposed > before and I heard no arguments against going this route > ... > > One suggestion that fixes this problem is to forbid bare template strings > without policy-prefixes and instead introduce some policy that returns > TemplateString itself. > > ```` > StringTemplate tmpl = TMPL."\{x} plus \{y} equals \{x + y}" ; > String s1 = tmpl.apply(STR); > String s2 = STR."\{x} plus \{y} equals \{x + y}"; > assertEquals(s1, s2); > ```` > > In this world the following can be observed: > > ```` > System.out.println(STR."\{x} plus \{y} equals \{x + y}"); // Prints > "10 plus 20 equals 30" > System.out.println(TMPL."\{x} plus \{y} equals \{x + y}"); // Prints > "StringTemplate{segments = ["", " plus ", " equals "], values = [10, 20, > 30]}" > System.out.println("\{x} plus \{y} equals \{x + y}"); // COMPILATION > ERROR: error: unrecognized escape sequence '\{', policy is required for > template strings > ```` > Sounds really good to me. In any case, template expression is not directly substitutable. E.g. you cannot replace STR."Hello \{user}" with StringTemplate t = "Hello \{user}" STR.t Instead, you need to desugar to a method call, like STR.process(t). I think that in Java, all the expressions we had before were substitutable in this sense: you could extract them to variable, and use that variable instead. So it would be more consistent to avoid naming the right part of process template expression as expression itself. I would suggest the following: If parser meets a dot that follows single double quote, or triple double quote, then the right part is always a string template, or a text blocks template, and the whole thing including qualifier is process template expression. Template may have no substitutions at all, in this case it looks exactly like string or text block, but it's a template. Template is not an expression and cannot appear in any context other than in process template expression. This simplifies grammar somewhat, as we say that template without placeholders is still a template. Also, having an identity template processor, like TMPL proposed by Victor, makes it possible and consistent to instantiate the StringTemplate object that contains no placeholders. The cost is five characters (TMPL.) necessary for presumably rare and advanced uses of templates where one wants to store it without immediate processing. What do you think? Tagir Valeev -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Fri Oct 21 21:03:00 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Fri, 21 Oct 2022 21:03:00 +0000 Subject: [External] : Re: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: Message-ID: <8D480BE7-5EE3-4212-B33D-50A8B39C6200@oracle.com> It?s not unreasonable. In fact, the early POCs had a RAW processor. I would still like to see how it plays out as-is during Preview. Others might develop other opinions or ideas. Cheers, ? Jim ? On Oct 21, 2022, at 5:34 PM, Tagir Valeev wrote: ? Hello! On Fri, Oct 21, 2022, 15:05 Victor Nazarov > wrote: Hello! I think one suggestion that fixes stated problems was already proposed before and I heard no arguments against going this route ... One suggestion that fixes this problem is to forbid bare template strings without policy-prefixes and instead introduce some policy that returns TemplateString itself. ```` StringTemplate tmpl = TMPL."\{x} plus \{y} equals \{x + y}" ; String s1 = tmpl.apply(STR); String s2 = STR."\{x} plus \{y} equals \{x + y}"; assertEquals(s1, s2); ```` In this world the following can be observed: ```` System.out.println(STR."\{x} plus \{y} equals \{x + y}"); // Prints "10 plus 20 equals 30" System.out.println(TMPL."\{x} plus \{y} equals \{x + y}"); // Prints "StringTemplate{segments = ["", " plus ", " equals "], values = [10, 20, 30]}" System.out.println("\{x} plus \{y} equals \{x + y}"); // COMPILATION ERROR: error: unrecognized escape sequence '\{', policy is required for template strings ```` Sounds really good to me. In any case, template expression is not directly substitutable. E.g. you cannot replace STR."Hello \{user}" with StringTemplate t = "Hello \{user}" STR.t Instead, you need to desugar to a method call, like STR.process(t). I think that in Java, all the expressions we had before were substitutable in this sense: you could extract them to variable, and use that variable instead. So it would be more consistent to avoid naming the right part of process template expression as expression itself. I would suggest the following: If parser meets a dot that follows single double quote, or triple double quote, then the right part is always a string template, or a text blocks template, and the whole thing including qualifier is process template expression. Template may have no substitutions at all, in this case it looks exactly like string or text block, but it's a template. Template is not an expression and cannot appear in any context other than in process template expression. This simplifies grammar somewhat, as we say that template without placeholders is still a template. Also, having an identity template processor, like TMPL proposed by Victor, makes it possible and consistent to instantiate the StringTemplate object that contains no placeholders. The cost is five characters (TMPL.) necessary for presumably rare and advanced uses of templates where one wants to store it without immediate processing. What do you think? Tagir Valeev -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Fri Oct 21 21:17:31 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Fri, 21 Oct 2022 21:17:31 +0000 Subject: [External] : Re: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: <8D480BE7-5EE3-4212-B33D-50A8B39C6200@oracle.com> References: <8D480BE7-5EE3-4212-B33D-50A8B39C6200@oracle.com> Message-ID: I suppose I should have mentioned you can do this by convention as well. Define RAW and use to highlight where StringTemplates are needed. TemplateProcessor RAW = st -> st; StringTemplate template = RAW.???; ? On Oct 21, 2022, at 6:03 PM, Jim Laskey wrote: ? It?s not unreasonable. In fact, the early POCs had a RAW processor. I would still like to see how it plays out as-is during Preview. Others might develop other opinions or ideas. Cheers, ? Jim ? On Oct 21, 2022, at 5:34 PM, Tagir Valeev wrote: ? Hello! On Fri, Oct 21, 2022, 15:05 Victor Nazarov > wrote: Hello! I think one suggestion that fixes stated problems was already proposed before and I heard no arguments against going this route ... One suggestion that fixes this problem is to forbid bare template strings without policy-prefixes and instead introduce some policy that returns TemplateString itself. ```` StringTemplate tmpl = TMPL."\{x} plus \{y} equals \{x + y}" ; String s1 = tmpl.apply(STR); String s2 = STR."\{x} plus \{y} equals \{x + y}"; assertEquals(s1, s2); ```` In this world the following can be observed: ```` System.out.println(STR."\{x} plus \{y} equals \{x + y}"); // Prints "10 plus 20 equals 30" System.out.println(TMPL."\{x} plus \{y} equals \{x + y}"); // Prints "StringTemplate{segments = ["", " plus ", " equals "], values = [10, 20, 30]}" System.out.println("\{x} plus \{y} equals \{x + y}"); // COMPILATION ERROR: error: unrecognized escape sequence '\{', policy is required for template strings ```` Sounds really good to me. In any case, template expression is not directly substitutable. E.g. you cannot replace STR."Hello \{user}" with StringTemplate t = "Hello \{user}" STR.t Instead, you need to desugar to a method call, like STR.process(t). I think that in Java, all the expressions we had before were substitutable in this sense: you could extract them to variable, and use that variable instead. So it would be more consistent to avoid naming the right part of process template expression as expression itself. I would suggest the following: If parser meets a dot that follows single double quote, or triple double quote, then the right part is always a string template, or a text blocks template, and the whole thing including qualifier is process template expression. Template may have no substitutions at all, in this case it looks exactly like string or text block, but it's a template. Template is not an expression and cannot appear in any context other than in process template expression. This simplifies grammar somewhat, as we say that template without placeholders is still a template. Also, having an identity template processor, like TMPL proposed by Victor, makes it possible and consistent to instantiate the StringTemplate object that contains no placeholders. The cost is five characters (TMPL.) necessary for presumably rare and advanced uses of templates where one wants to store it without immediate processing. What do you think? Tagir Valeev -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Oct 22 06:09:17 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Sat, 22 Oct 2022 08:09:17 +0200 (CEST) Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> Message-ID: <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> > From: "Gavin Bierman" > To: "amber-spec-experts" > Sent: Wednesday, October 19, 2022 12:22:55 AM > Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) > and Second Preview of Record Patterns (JEP 432) now available > Dear experts: > The first draft of a joint spec covering JEP 433 (Fourth Preview of Pattern > Matching for switch) and JEP 432 (Record Patterns) is available at: > [ https://cr.openjdk.java.net/~gbierman/jep432+433/latest | > https://cr.openjdk.java.net/~gbierman/jep432+433/latest ] > This covers all the changes for these JEPs *apart* from the proposal to infer > type arguments in patterns. This will be added and announced shortly. > Comments welcomed! > Gavin Still very disappointed that i can not express that i want the compiler to not compile a switch if a record has a new record component. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Oct 22 14:28:50 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 22 Oct 2022 10:28:50 -0400 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> Message-ID: What exactly do you mean by this? On 10/22/2022 2:09 AM, Remi Forax wrote: > > Still very disappointed that i can not express that i want the > compiler to not compile a switch if a record has a new record component. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From robbepincket at live.be Sat Oct 22 17:33:03 2022 From: robbepincket at live.be (Robbe Pincket) Date: Sat, 22 Oct 2022 17:33:03 +0000 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> Message-ID: Hi It seems like there isn?t anything about definite (un)assignedness in guard expressions? I was also wondering why one can only use (effectivly) final variables in a guard expression. Greetings Robbe Pincket From: Gavin Bierman Sent: woensdag 19 oktober 2022 0:23 To: amber-spec-experts Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available Dear experts: The first draft of a joint spec covering JEP 433 (Fourth Preview of Pattern Matching for switch) and JEP 432 (Record Patterns) is available at: https://cr.openjdk.java.net/~gbierman/jep432+433/latest This covers all the changes for these JEPs *apart* from the proposal to infer type arguments in patterns. This will be added and announced shortly. Comments welcomed! Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Oct 22 21:52:06 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sat, 22 Oct 2022 23:52:06 +0200 (CEST) Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> Message-ID: <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" , "Gavin Bierman" > Cc: "amber-spec-experts" > Sent: Saturday, October 22, 2022 4:28:50 PM > Subject: Re: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP > 433) and Second Preview of Record Patterns (JEP 432) now available > What exactly do you mean by this? If i modify a record by adding a new component, i want to compiler to help me to find all the switches that are using that record so i can re-evaulate if the new component play a role or not for each of those codes. Then for each code, i can say, i do not care about that new component by adding an any pattern or i care about it, add a binding and change/fix the code using that binding. That a power that the record pattern has over the type pattern, a type pattern does not check if the record has changed since the code was written. R?mi > On 10/22/2022 2:09 AM, Remi Forax wrote: >> Still very disappointed that i can not express that i want the compiler to not >> compile a switch if a record has a new record component. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Oct 24 01:17:39 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 23 Oct 2022 21:17:39 -0400 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> Message-ID: <64c1ff53-e228-3a56-8e66-256d21c16462@oracle.com> > What exactly do you mean by this? > > > If i modify a record by adding a new component, i want to compiler to > help me to find all the switches that are using that record so i can > re-evaulate if the new component play a role or not for each of those > codes. What you're asking for is cool, but enormous, because it involves introducing a new dimension into the programming model -- time. Currently, we identify which modifications are binary- or source- compatible, and let people make their choices, but the ways in which a program has evolved over time is external to the programming model. Adding a component (and doing nothing else) is technically neither a binary- nor source-compatible change.? (Existing constructor invocations will fail to link.)? Of course, you can add? a constructor overload for the old description -- and soon enough, you'll be able to add a deconstructor overload too. > Then for each code, i can say, i do not care about that new component > by adding an any pattern or i care about it, add a binding and > change/fix the code using that binding. The plan for dealing with this is the same as with the constructor: write a deconstructor for the old description.? Then all the existing match sites will continue to work, and you can use "find usages" to decide which ones you want to migrate. -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Oct 24 07:24:49 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Mon, 24 Oct 2022 09:24:49 +0200 (CEST) Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <64c1ff53-e228-3a56-8e66-256d21c16462@oracle.com> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> <64c1ff53-e228-3a56-8e66-256d21c16462@oracle.com> Message-ID: <1368558859.32258425.1666596289024.JavaMail.zimbra@u-pem.fr> > From: "Brian Goetz" > To: "Remi Forax" > Cc: "Gavin Bierman" , "amber-spec-experts" > > Sent: Monday, October 24, 2022 3:17:39 AM > Subject: Re: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP > 433) and Second Preview of Record Patterns (JEP 432) now available >>> What exactly do you mean by this? >> If i modify a record by adding a new component, i want to compiler to help me to >> find all the switches that are using that record so i can re-evaulate if the >> new component play a role or not for each of those codes. > What you're asking for is cool, but enormous, because it involves introducing a > new dimension into the programming model -- time. Currently, we identify which > modifications are binary- or source- compatible, and let people make their > choices, but the ways in which a program has evolved over time is external to > the programming model. The relation to time is not something directly written in the spec but Java has a long tradition of offering tools to deal with the maintenance of libraries and applications. I'm not proposing to do more, but like we support the use cases related to of maintaining APIs through time i believe we should also support the use cases related to maintaining data through time. And obviously, because those are use cases supported by the language not more, people will be free to program that way or not. > Adding a component (and doing nothing else) is technically neither a binary- nor > source-compatible change. (Existing constructor invocations will fail to link.) > Of course, you can add a constructor overload for the old description -- and > soon enough, you'll be able to add a deconstructor overload too. That's why Java provides reflection or bytecode transformation or annotation processors, which are used to decouple de data description from the data implementation. As an example, frameworks like Spring, Quarkus, Micronaut or Helidon, all provide a way to map JSON objects to records in a loosely coupled way allowing a kind of backward compatibility. >> Then for each code, i can say, i do not care about that new component by adding >> an any pattern or i care about it, add a binding and change/fix the code using >> that binding. > The plan for dealing with this is the same as with the constructor: write a > deconstructor for the old description. Then all the existing match sites will > continue to work, and you can use "find usages" to decide which ones you want > to migrate. In the case of APIs, we want source/binary backward compatibility, but may not necessarily want the same in case of data, because data may not cross boundaries. Using the same example of modern Java frameworks, all those frameworks already define boundaries for the user, how to define a REST API is already defined, how to interact with a database is already defined, etc, so as a user you are already inside an application with well defined boundaries. In that case, you do not care too much about data backward compatibility but more about having the data correctly describing the business and how to update those data when the business requirements change. I'm not proposing to change the language model to introduce the notion of time but to be able to support use cases where the data evolve through time. In my mind, i call that approach "data first". We already have features that goes in that direction, sealed types + switch exhaustiveness is a great way to help users to find what code should be upgraded when a new kind of data need to be added. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From james.laskey at oracle.com Mon Oct 24 11:58:29 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Mon, 24 Oct 2022 11:58:29 +0000 Subject: [External] : Re: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: <8D480BE7-5EE3-4212-B33D-50A8B39C6200@oracle.com> Message-ID: Tagir, I?ve added RAW as a standard template processor. It is not statically imported automatically (import static java.lang.template.StringTemplate.RAW; required). I would recommend that ?on inspection? giving the user the choice of; 1. STR."..." 2. RAW."..." 3. //noinspection IntentionalRawStringTemplate (or somesuch) "..." The intention of the statement below is clear. Would inspection ignore this case? StringTemplate st = "..."; Cheers, ? Jim On Oct 21, 2022, at 6:17 PM, Jim Laskey > wrote: I suppose I should have mentioned you can do this by convention as well. Define RAW and use to highlight where StringTemplates are needed. TemplateProcessor RAW = st -> st; StringTemplate template = RAW.???; ? On Oct 21, 2022, at 6:03 PM, Jim Laskey > wrote: ? It?s not unreasonable. In fact, the early POCs had a RAW processor. I would still like to see how it plays out as-is during Preview. Others might develop other opinions or ideas. Cheers, ? Jim ? On Oct 21, 2022, at 5:34 PM, Tagir Valeev > wrote: ? Hello! On Fri, Oct 21, 2022, 15:05 Victor Nazarov > wrote: Hello! I think one suggestion that fixes stated problems was already proposed before and I heard no arguments against going this route ... One suggestion that fixes this problem is to forbid bare template strings without policy-prefixes and instead introduce some policy that returns TemplateString itself. ```` StringTemplate tmpl = TMPL."\{x} plus \{y} equals \{x + y}" ; String s1 = tmpl.apply(STR); String s2 = STR."\{x} plus \{y} equals \{x + y}"; assertEquals(s1, s2); ```` In this world the following can be observed: ```` System.out.println(STR."\{x} plus \{y} equals \{x + y}"); // Prints "10 plus 20 equals 30" System.out.println(TMPL."\{x} plus \{y} equals \{x + y}"); // Prints "StringTemplate{segments = ["", " plus ", " equals "], values = [10, 20, 30]}" System.out.println("\{x} plus \{y} equals \{x + y}"); // COMPILATION ERROR: error: unrecognized escape sequence '\{', policy is required for template strings ```` Sounds really good to me. In any case, template expression is not directly substitutable. E.g. you cannot replace STR."Hello \{user}" with StringTemplate t = "Hello \{user}" STR.t Instead, you need to desugar to a method call, like STR.process(t). I think that in Java, all the expressions we had before were substitutable in this sense: you could extract them to variable, and use that variable instead. So it would be more consistent to avoid naming the right part of process template expression as expression itself. I would suggest the following: If parser meets a dot that follows single double quote, or triple double quote, then the right part is always a string template, or a text blocks template, and the whole thing including qualifier is process template expression. Template may have no substitutions at all, in this case it looks exactly like string or text block, but it's a template. Template is not an expression and cannot appear in any context other than in process template expression. This simplifies grammar somewhat, as we say that template without placeholders is still a template. Also, having an identity template processor, like TMPL proposed by Victor, makes it possible and consistent to instantiate the StringTemplate object that contains no placeholders. The cost is five characters (TMPL.) necessary for presumably rare and advanced uses of templates where one wants to store it without immediate processing. What do you think? Tagir Valeev -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Oct 24 16:25:52 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 24 Oct 2022 18:25:52 +0200 Subject: [External] : Re: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: <8D480BE7-5EE3-4212-B33D-50A8B39C6200@oracle.com> Message-ID: Thanks! On Mon, Oct 24, 2022 at 1:58 PM Jim Laskey wrote: > > Tagir, > > I?ve added RAW as a standard template processor. It is not statically imported automatically (import static java.lang.template.StringTemplate.RAW; required). I would recommend that ?on inspection? giving the user the choice of; > > 1. > STR."..." > 2. > RAW."..." > 3. > //noinspection IntentionalRawStringTemplate (or somesuch) > "..." > > The intention of the statement below is clear. Would inspection ignore this case? > > StringTemplate st = "..."; > > Cheers, > > ? Jim > > > > > On Oct 21, 2022, at 6:17 PM, Jim Laskey wrote: > > I suppose I should have mentioned you can do this by convention as well. Define RAW and use to highlight where StringTemplates are needed. > > TemplateProcessor RAW = st -> st; > StringTemplate template = RAW.???; > > ? > > On Oct 21, 2022, at 6:03 PM, Jim Laskey wrote: > > ? It?s not unreasonable. In fact, the early POCs had a RAW processor. > > I would still like to see how it plays out as-is during Preview. Others might develop other opinions or ideas. > > Cheers, > > ? Jim > ? > > On Oct 21, 2022, at 5:34 PM, Tagir Valeev wrote: > > ? > Hello! > > On Fri, Oct 21, 2022, 15:05 Victor Nazarov wrote: >> >> Hello! >> >> I think one suggestion that fixes stated problems was already proposed before and I heard no arguments against going this route >> ... >> >> One suggestion that fixes this problem is to forbid bare template strings without policy-prefixes and instead introduce some policy that returns TemplateString itself. >> >> ```` >> StringTemplate tmpl = TMPL."\{x} plus \{y} equals \{x + y}" ; >> String s1 = tmpl.apply(STR); >> String s2 = STR."\{x} plus \{y} equals \{x + y}"; >> assertEquals(s1, s2); >> ```` >> >> In this world the following can be observed: >> >> ```` >> System.out.println(STR."\{x} plus \{y} equals \{x + y}"); // Prints "10 plus 20 equals 30" >> System.out.println(TMPL."\{x} plus \{y} equals \{x + y}"); // Prints "StringTemplate{segments = ["", " plus ", " equals "], values = [10, 20, 30]}" >> System.out.println("\{x} plus \{y} equals \{x + y}"); // COMPILATION ERROR: error: unrecognized escape sequence '\{', policy is required for template strings >> ```` > > > Sounds really good to me. In any case, template expression is not directly substitutable. E.g. you cannot replace > > STR."Hello \{user}" > > with > > StringTemplate t = "Hello \{user}" > STR.t > > Instead, you need to desugar to a method call, like STR.process(t). I think that in Java, all the expressions we had before were substitutable in this sense: you could extract them to variable, and use that variable instead. So it would be more consistent to avoid naming the right part of process template expression as expression itself. I would suggest the following: > > If parser meets a dot that follows single double quote, or triple double quote, then the right part is always a string template, or a text blocks template, and the whole thing including qualifier is process template expression. Template may have no substitutions at all, in this case it looks exactly like string or text block, but it's a template. Template is not an expression and cannot appear in any context other than in process template expression. > > This simplifies grammar somewhat, as we say that template without placeholders is still a template. Also, having an identity template processor, like TMPL proposed by Victor, makes it possible and consistent to instantiate the StringTemplate object that contains no placeholders. The cost is five characters (TMPL.) necessary for presumably rare and advanced uses of templates where one wants to store it without immediate processing. > > What do you think? > Tagir Valeev > > From amaembo at gmail.com Mon Oct 24 16:26:32 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 24 Oct 2022 18:26:32 +0200 Subject: [External] : Re: [string-templates] Accidental use of StringTemplate instead of string In-Reply-To: References: <8D480BE7-5EE3-4212-B33D-50A8B39C6200@oracle.com> Message-ID: On Mon, Oct 24, 2022 at 1:58 PM Jim Laskey wrote: > The intention of the statement below is clear. Would inspection ignore this case? > > StringTemplate st = "..."; Sure, in this case no inspection warning would be issued. > > Cheers, > > ? Jim > > > > > On Oct 21, 2022, at 6:17 PM, Jim Laskey wrote: > > I suppose I should have mentioned you can do this by convention as well. Define RAW and use to highlight where StringTemplates are needed. > > TemplateProcessor RAW = st -> st; > StringTemplate template = RAW.???; > > ? > > On Oct 21, 2022, at 6:03 PM, Jim Laskey wrote: > > ? It?s not unreasonable. In fact, the early POCs had a RAW processor. > > I would still like to see how it plays out as-is during Preview. Others might develop other opinions or ideas. > > Cheers, > > ? Jim > ? > > On Oct 21, 2022, at 5:34 PM, Tagir Valeev wrote: > > ? > Hello! > > On Fri, Oct 21, 2022, 15:05 Victor Nazarov wrote: >> >> Hello! >> >> I think one suggestion that fixes stated problems was already proposed before and I heard no arguments against going this route >> ... >> >> One suggestion that fixes this problem is to forbid bare template strings without policy-prefixes and instead introduce some policy that returns TemplateString itself. >> >> ```` >> StringTemplate tmpl = TMPL."\{x} plus \{y} equals \{x + y}" ; >> String s1 = tmpl.apply(STR); >> String s2 = STR."\{x} plus \{y} equals \{x + y}"; >> assertEquals(s1, s2); >> ```` >> >> In this world the following can be observed: >> >> ```` >> System.out.println(STR."\{x} plus \{y} equals \{x + y}"); // Prints "10 plus 20 equals 30" >> System.out.println(TMPL."\{x} plus \{y} equals \{x + y}"); // Prints "StringTemplate{segments = ["", " plus ", " equals "], values = [10, 20, 30]}" >> System.out.println("\{x} plus \{y} equals \{x + y}"); // COMPILATION ERROR: error: unrecognized escape sequence '\{', policy is required for template strings >> ```` > > > Sounds really good to me. In any case, template expression is not directly substitutable. E.g. you cannot replace > > STR."Hello \{user}" > > with > > StringTemplate t = "Hello \{user}" > STR.t > > Instead, you need to desugar to a method call, like STR.process(t). I think that in Java, all the expressions we had before were substitutable in this sense: you could extract them to variable, and use that variable instead. So it would be more consistent to avoid naming the right part of process template expression as expression itself. I would suggest the following: > > If parser meets a dot that follows single double quote, or triple double quote, then the right part is always a string template, or a text blocks template, and the whole thing including qualifier is process template expression. Template may have no substitutions at all, in this case it looks exactly like string or text block, but it's a template. Template is not an expression and cannot appear in any context other than in process template expression. > > This simplifies grammar somewhat, as we say that template without placeholders is still a template. Also, having an identity template processor, like TMPL proposed by Victor, makes it possible and consistent to instantiate the StringTemplate object that contains no placeholders. The cost is five characters (TMPL.) necessary for presumably rare and advanced uses of templates where one wants to store it without immediate processing. > > What do you think? > Tagir Valeev > > From forax at univ-mlv.fr Mon Oct 24 16:53:32 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 24 Oct 2022 18:53:32 +0200 (CEST) Subject: Record pattern and enhanced for Message-ID: <1660079902.32745282.1666630412594.JavaMail.zimbra@u-pem.fr> Hi do not think we have yet discussed about using the record pattern in an enhanced for https://cr.openjdk.java.net/~gbierman/jep432%2b433/jep432+433-20221018/specs/patterns-switch-record-patterns-jls.html#jls-14.14.2 My first reaction when reading that part of the spec was, ok, cool, a bit weird to not piggyback it on pattern assignment but on the switch instead. But this morning in the shower, it was more, wait a minute ... it means that a record pattern and a type declaration using the same type do not work the same. For example, with a sealed interface I and a record Impl(int value), it's sole implementation. With the proposed semantics, you can write I i = ... for(Impl(var value) : List.of(i)) { // use a record pattern System.out.println(value); } but you can not write I i = ... for(Impl impl : List.of(i)) { // use a type declaration, does not compile ! System.out.println(impl.value()); } I hope we can keep the invariant that a record pattern and a variable declaration with the same type should both work the same way. There is also a minor issue that looks like an overlook but refactoring an exhanced for from using a declaration to a record pattern is a little dangerous because if the iterable is null the semantics change, with the record pattern it throws a MatchException instead of a NPE. regards, R?mi From brian.goetz at oracle.com Mon Oct 24 17:06:15 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 24 Oct 2022 13:06:15 -0400 Subject: Record pattern and enhanced for In-Reply-To: <1660079902.32745282.1666630412594.JavaMail.zimbra@u-pem.fr> References: <1660079902.32745282.1666630412594.JavaMail.zimbra@u-pem.fr> Message-ID: > Hi do not think we have yet discussed about using the record pattern in an enhanced for > https://cr.openjdk.java.net/~gbierman/jep432%2b433/jep432+433-20221018/specs/patterns-switch-record-patterns-jls.html#jls-14.14.2 > > My first reaction when reading that part of the spec was, ok, cool, a bit weird to not piggyback it on pattern assignment but on the switch instead. It's not piggybacked on either.? It's part of the Record Patterns JEP, which lets you put record patterns in instanceof, switch, and foreach contexts.? (The semantics are indeed similar to pattern assignment, so when that comes along, nothing new to learn.) > But this morning in the shower, it was more, wait a minute ... it means that a record pattern and a type declaration using the same type do not work the same. > > For example, with a sealed interface I and a record Impl(int value), it's sole implementation. Yes.? More generally, patterns are richer than assignment, and the semantics of foreach currently draws off of assignment semantics. We're currently investigating the details to broaden the interpretation of `for (T t : e)` to interpret `T t` as a type pattern, rather than the ad-hoc thing it currently is; we'll report back. > I hope we can keep the invariant that a record pattern and a variable declaration with the same type should both work the same way. This is not about type patterns vs record patterns; the asymmetry you see here is about patterns vs non-patterns in `foreach`.? The path to fixing this should be to treat them all as patterns. > There is also a minor issue that looks like an overlook but refactoring an exhanced for from using a declaration to a record pattern is a little dangerous because if the iterable is null the semantics change, with the record pattern it throws a MatchException instead of a NPE. Yeah, it might make sense to special-case null here, though the MatchException has to stay in the general case of "looked exhaustive at compile time, but wasn't at run time." From robbepincket at live.be Mon Oct 24 17:45:56 2022 From: robbepincket at live.be (Robbe Pincket) Date: Mon, 24 Oct 2022 17:45:56 +0000 Subject: Record pattern and enhanced for In-Reply-To: References: <1660079902.32745282.1666630412594.JavaMail.zimbra@u-pem.fr> Message-ID: >> There is also a minor issue that looks like an overlook but refactoring an exhanced for from using a declaration to a record pattern is a little dangerous because if the iterable is null the semantics change, with the record pattern it throws a MatchException instead of a NPE. > > Yeah, it might make sense to special-case null here, though the > MatchException has to stay in the general case of "looked exhaustive at > compile time, but wasn't at run time." I'm quite confused here. I don't see any mention of foreach throwing an MatchException instead of NPE when the iterable is null. I also don't understand the reasoning for making the foreach throw a MatchException when one of the items is null instead of a simple NPE. Regards Robbe Pincket -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Oct 26 09:42:00 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Wed, 26 Oct 2022 11:42:00 +0200 (CEST) Subject: Record pattern and enhanced for In-Reply-To: References: <1660079902.32745282.1666630412594.JavaMail.zimbra@u-pem.fr> Message-ID: <2095459263.34327872.1666777320422.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Brian Goetz" > To: "Remi Forax" , "amber-spec-experts" > Sent: Monday, October 24, 2022 7:06:15 PM > Subject: Re: Record pattern and enhanced for >> Hi do not think we have yet discussed about using the record pattern in an >> enhanced for >> https://cr.openjdk.java.net/~gbierman/jep432%2b433/jep432+433-20221018/specs/patterns-switch-record-patterns-jls.html#jls-14.14.2 >> >> My first reaction when reading that part of the spec was, ok, cool, a bit weird >> to not piggyback it on pattern assignment but on the switch instead. > > It's not piggybacked on either.? It's part of the Record Patterns JEP, > which lets you put record patterns in instanceof, switch, and foreach > contexts.? (The semantics are indeed similar to pattern assignment, so > when that comes along, nothing new to learn.) The thing is that there are two ways to implement assignment + pattern, one is to consider that the pattern should be total on the type of the expression, the other is that it should be equivalent to a switch. The later does not work well because of the remainders (null + new subtypes due to separate compilation). I believe we have made the right call for switch to not force users to explicitly write the handling of the remainders but we can use a more strict approach for the assignment / inside an enhanced for. R?mi From james.laskey at oracle.com Thu Oct 27 19:19:37 2022 From: james.laskey at oracle.com (Jim Laskey) Date: Thu, 27 Oct 2022 19:19:37 +0000 Subject: String template always requiring a processor Message-ID: <76601683-C09E-4FB7-8B30-A967C8BCEE49@oracle.com> We?ve been having a rethink after recent concerns expressed by several experts over the current string template design leading beginners down the path only to stubble over "string templates are not strings?. Our original view was that users would soon learn to insert the STR, but after consideration, having the surprise hit them at runtime is not a compelling selling point. Consequently, we are changing things up to follow Tagir?s suggestion of always requiring a processor; i.e., no naked StringTemplates. As a result, IDEs can flag the problem when editing or have inspection insert the STR automatically. For most users, STR would have been used anyway. So, this change really only affects those requiring the unprocessed string template (me and one or two other people). In those cases, users can use the RAW processor. Making it easier for IDEs to detect and correct will help mute the grumbling about naked StringTemplates not being string interpolation. As stated in the JEP, making Java safe and secure is the goal. This is the way. Cheers, ? Jim From guy.steele at oracle.com Thu Oct 27 19:35:49 2022 From: guy.steele at oracle.com (Guy Steele) Date: Thu, 27 Oct 2022 19:35:49 +0000 Subject: String template always requiring a processor In-Reply-To: <76601683-C09E-4FB7-8B30-A967C8BCEE49@oracle.com> References: <76601683-C09E-4FB7-8B30-A967C8BCEE49@oracle.com> Message-ID: <819283C5-235C-4222-B4C2-53A8C5BCDFBB@oracle.com> I agree that, because one can always use the RAW processor (which simply passes back the string template itself), always requiring a processor syntactically seems like a good way to go. ?Guy > On Oct 27, 2022, at 3:19 PM, Jim Laskey wrote: > > We?ve been having a rethink after recent concerns expressed by several experts over the current string template design leading beginners down the path only to stubble over "string templates are not strings?. Our original view was that users would soon learn to insert the STR, but after consideration, having the surprise hit them at runtime is not a compelling selling point. > > Consequently, we are changing things up to follow Tagir?s suggestion of always requiring a processor; i.e., no naked StringTemplates. As a result, IDEs can flag the problem when editing or have inspection insert the STR automatically. For most users, STR would have been used anyway. So, this change really only affects those requiring the unprocessed string template (me and one or two other people). In those cases, users can use the RAW processor. > > Making it easier for IDEs to detect and correct will help mute the grumbling about naked StringTemplates not being string interpolation. As stated in the JEP, making Java safe and secure is the goal. This is the way. > > Cheers, > > ? Jim > From amaembo at gmail.com Thu Oct 27 21:06:19 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 27 Oct 2022 23:06:19 +0200 Subject: String template always requiring a processor In-Reply-To: <819283C5-235C-4222-B4C2-53A8C5BCDFBB@oracle.com> References: <76601683-C09E-4FB7-8B30-A967C8BCEE49@oracle.com> <819283C5-235C-4222-B4C2-53A8C5BCDFBB@oracle.com> Message-ID: Hello! Glad to hear, I fully support this decision. A correction: this was suggested by Victor Nazarov. I only formulated the problem but did not post the solution. With best regards, Tagir Valeev. On Thu, Oct 27, 2022, 21:35 Guy Steele wrote: > I agree that, because one can always use the RAW processor (which simply > passes back the string template itself), always requiring a processor > syntactically seems like a good way to go. > > ?Guy > > > On Oct 27, 2022, at 3:19 PM, Jim Laskey wrote: > > > > We?ve been having a rethink after recent concerns expressed by several > experts over the current string template design leading beginners down the > path only to stubble over "string templates are not strings?. Our original > view was that users would soon learn to insert the STR, but after > consideration, having the surprise hit them at runtime is not a compelling > selling point. > > > > Consequently, we are changing things up to follow Tagir?s suggestion of > always requiring a processor; i.e., no naked StringTemplates. As a result, > IDEs can flag the problem when editing or have inspection insert the STR > automatically. For most users, STR would have been used anyway. So, this > change really only affects those requiring the unprocessed string template > (me and one or two other people). In those cases, users can use the RAW > processor. > > > > Making it easier for IDEs to detect and correct will help mute the > grumbling about naked StringTemplates not being string interpolation. As > stated in the JEP, making Java safe and secure is the goal. This is the way. > > > > Cheers, > > > > ? Jim > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Thu Oct 27 21:10:10 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Thu, 27 Oct 2022 23:10:10 +0200 (CEST) Subject: String template always requiring a processor In-Reply-To: <819283C5-235C-4222-B4C2-53A8C5BCDFBB@oracle.com> References: <76601683-C09E-4FB7-8B30-A967C8BCEE49@oracle.com> <819283C5-235C-4222-B4C2-53A8C5BCDFBB@oracle.com> Message-ID: <70069134.35475951.1666905010834.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "Guy Steele" > To: "Jim Laskey" > Cc: "amber-spec-experts" > Sent: Thursday, October 27, 2022 9:35:49 PM > Subject: Re: String template always requiring a processor > I agree that, because one can always use the RAW processor (which simply passes > back the string template itself), always requiring a processor syntactically > seems like a good way to go. > > ?Guy yes, I believe RAW can be written private static final TemplateProcessor RAW = template -> template; > >> On Oct 27, 2022, at 3:19 PM, Jim Laskey wrote: >> >> We?ve been having a rethink after recent concerns expressed by several experts >> over the current string template design leading beginners down the path only to >> stubble over "string templates are not strings?. Our original view was that >> users would soon learn to insert the STR, but after consideration, having the >> surprise hit them at runtime is not a compelling selling point. >> >> Consequently, we are changing things up to follow Tagir?s suggestion of always >> requiring a processor; i.e., no naked StringTemplates. As a result, IDEs can >> flag the problem when editing or have inspection insert the STR automatically. >> For most users, STR would have been used anyway. So, this change really only >> affects those requiring the unprocessed string template (me and one or two >> other people). In those cases, users can use the RAW processor. >> >> Making it easier for IDEs to detect and correct will help mute the grumbling >> about naked StringTemplates not being string interpolation. As stated in the >> JEP, making Java safe and secure is the goal. This is the way. Hi Jim, thanks for listening, regards. >> >> Cheers, >> >> ? Jim R?mi From daniel.smith at oracle.com Thu Oct 27 21:51:23 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 27 Oct 2022 21:51:23 +0000 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> Message-ID: <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> > On Oct 22, 2022, at 2:52 PM, forax at univ-mlv.fr wrote: > > If i modify a record by adding a new component, i want to compiler to help me to find all the switches that are using that record so i can re-evaulate if the new component play a role or not for each of those codes. Maybe I'm missing something, but doesn't it do this already? "The length of record component pattern list must be the same as the length of the record component list in the declaration of the record class named by ReferenceType; otherwise a compile-time error occurs." (14.30.1) It would be one thing if you want runtime checks, but you're just saying you want a compiler error, right? From forax at univ-mlv.fr Thu Oct 27 22:03:18 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 28 Oct 2022 00:03:18 +0200 (CEST) Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> Message-ID: <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "daniel smith" > To: "Remi Forax" > Cc: "Brian Goetz" , "Gavin Bierman" , "amber-spec-experts" > > Sent: Thursday, October 27, 2022 11:51:23 PM > Subject: Re: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record > Patterns (JEP 432) now available >> On Oct 22, 2022, at 2:52 PM, forax at univ-mlv.fr wrote: >> >> If i modify a record by adding a new component, i want to compiler to help me to >> find all the switches that are using that record so i can re-evaulate if the >> new component play a role or not for each of those codes. > > Maybe I'm missing something, but doesn't it do this already? yes, but with the proposed semantics, it's not possible to extract the record instance AND have the length of the record component list checked. Either i can use case Point p -> or case Point(var x, var y) -> but this is not valid anymore case Point(var x, var y) p -> ... R?mi From daniel.smith at oracle.com Thu Oct 27 22:11:49 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 27 Oct 2022 22:11:49 +0000 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> Message-ID: <4BC33B28-5A61-4FFE-9EB7-039BCD98E91A@oracle.com> > On Oct 27, 2022, at 3:03 PM, forax at univ-mlv.fr wrote: > > ----- Original Message ----- >> From: "daniel smith" >> To: "Remi Forax" >> Cc: "Brian Goetz" , "Gavin Bierman" , "amber-spec-experts" >> >> Sent: Thursday, October 27, 2022 11:51:23 PM >> Subject: Re: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record >> Patterns (JEP 432) now available > >>> On Oct 22, 2022, at 2:52 PM, forax at univ-mlv.fr wrote: >>> >>> If i modify a record by adding a new component, i want to compiler to help me to >>> find all the switches that are using that record so i can re-evaulate if the >>> new component play a role or not for each of those codes. >> >> Maybe I'm missing something, but doesn't it do this already? > > yes, but with the proposed semantics, it's not possible to extract the record instance AND have the length of the record component list checked. > > Either i can use > case Point p -> > > or > case Point(var x, var y) -> > > but this is not valid anymore > case Point(var x, var y) p -> ... Can always do this, right? case Point p where p instanceof Point(var x, var y) -> From forax at univ-mlv.fr Thu Oct 27 22:25:05 2022 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 28 Oct 2022 00:25:05 +0200 (CEST) Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <4BC33B28-5A61-4FFE-9EB7-039BCD98E91A@oracle.com> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> <4BC33B28-5A61-4FFE-9EB7-039BCD98E91A@oracle.com> Message-ID: <1904405125.35479800.1666909505095.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "daniel smith" > To: "Remi Forax" > Cc: "Brian Goetz" , "Gavin Bierman" , "amber-spec-experts" > > Sent: Friday, October 28, 2022 12:11:49 AM > Subject: Re: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record > Patterns (JEP 432) now available >> On Oct 27, 2022, at 3:03 PM, forax at univ-mlv.fr wrote: >> >> ----- Original Message ----- >>> From: "daniel smith" >>> To: "Remi Forax" >>> Cc: "Brian Goetz" , "Gavin Bierman" >>> , "amber-spec-experts" >>> >>> Sent: Thursday, October 27, 2022 11:51:23 PM >>> Subject: Re: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP >>> 433) and Second Preview of Record >>> Patterns (JEP 432) now available >> >>>> On Oct 22, 2022, at 2:52 PM, forax at univ-mlv.fr wrote: >>>> >>>> If i modify a record by adding a new component, i want to compiler to help me to >>>> find all the switches that are using that record so i can re-evaulate if the >>>> new component play a role or not for each of those codes. >>> >>> Maybe I'm missing something, but doesn't it do this already? >> >> yes, but with the proposed semantics, it's not possible to extract the record >> instance AND have the length of the record component list checked. >> >> Either i can use >> case Point p -> >> >> or >> case Point(var x, var y) -> >> >> but this is not valid anymore >> case Point(var x, var y) p -> ... > > Can always do this, right? > > case Point p where p instanceof Point(var x, var y) -> Gavin already proposed that, in France, we have a sentence for that Why do it the easy way when you can do it the hard way ? [1] moreover this pattern+where is not exhaustive on Point. R?mi [1] https://en.wikipedia.org/wiki/Les_Shadoks From daniel.smith at oracle.com Thu Oct 27 22:58:56 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Thu, 27 Oct 2022 22:58:56 +0000 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <1904405125.35479800.1666909505095.JavaMail.zimbra@u-pem.fr> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> <4BC33B28-5A61-4FFE-9EB7-039BCD98E91A@oracle.com> <1904405125.35479800.1666909505095.JavaMail.zimbra@u-pem.fr> Message-ID: > On Oct 27, 2022, at 3:25 PM, forax at univ-mlv.fr wrote: > >>> Either i can use >>> case Point p -> >>> >>> or >>> case Point(var x, var y) -> >>> >>> but this is not valid anymore >>> case Point(var x, var y) p -> ... >> >> Can always do this, right? >> >> case Point p where p instanceof Point(var x, var y) -> > > Gavin already proposed that, in France, we have a sentence for that > Why do it the easy way when you can do it the hard way ? [1] I dunno, I guess I don't really understand the motivation. If you do 'case Point(var x, var y)', that solves your problem: won't compile if a new component comes along. If you do 'case Point p', it *will* compile, of course, just like if you declare a field of that type. It won't check the arity, because we don't check properties of classes based on a random mention of them. I guess you're really looking for something like 'case Point(#2#) p', some compact way to assert that the class you're talking about still has 2 record components. But... this is such a niche-sounding concern to me, I don't see why *records* are the one kind of class in Java for which such a feature would be important. In any case, good news?even though we don't have this dedicated feature, there are various ways you can mention the record class in a record pattern and get the compiler to make this check. Just costs a few more characters. ???? > moreover this pattern+where is not exhaustive on Point. Ah, that's interesting. I think we need to think more about what features or idioms we'll recommend for people to do assignment-like decomposition. I'm not sure that's settled yet. If we arrive at 'RecordType instanceof RecordPattern' as a common decomposition idiom, then I think it would be reasonable to expect the analysis of guards (and probably DA/DU, etc....) to special-case this and realize it will always be true. From brian.goetz at oracle.com Fri Oct 28 00:00:24 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 27 Oct 2022 20:00:24 -0400 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2090352005.31601144.1666418957595.JavaMail.zimbra@u-pem.fr> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> Message-ID: <9671998f-e8c6-a5c5-73a9-db4fdfda1687@oracle.com> > yes, but with the proposed semantics, it's not possible to extract the record instance AND have the length of the record component list checked. I think we've beaten this topic to death.? Yes, I get that you want this.? We can come back for it when we're happy with the story. From daniel.smith at oracle.com Fri Oct 28 00:51:26 2022 From: daniel.smith at oracle.com (Dan Smith) Date: Fri, 28 Oct 2022 00:51:26 +0000 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> Message-ID: <586AADA9-E4B6-4656-A68E-5436BC188A2B@oracle.com> I've reviewed the draft and put together a few patches, but here are a few more stray bits of feedback from me, most of which get into concepts and design: --- 5.5: There's this "downcast compatible" notion that was apparently introduced with instanceof pattern matching. It means a cast is legal and not unchecked. Ok. But?not all legal casts are downcasts, right? It's pretty confusing to use a term that suggests that they are. jshell> Serializable ser = "abc" ser ==> "abc" jshell> if (ser instanceof Comparable c) { System.out.println("matches");} matches --- 6.3.3.1: Maybe someday we'll have expressions nested in patterns, but for now isn't it meaningless to ask whether a pattern variable is in scope within another part of the same pattern? --- 13.4.2: I don't know if I'd talk about "migration compatibility" here. It's a rather common thing for a separately-compiled change to cause a runtime error that is not a linkage error. But previously the phrase "migration compatible" doesn't get mentioned in Chapter 13, and we don't go out of our way to label these conditions. It's just... a runtime exception. --- 14.11.1: I'm not sure the prohibition of 'when false' makes sense. We don't complain about unreachable code in 'if (false) { ... }', for example, or consider variables touched inside the 'if' to be DU. A possible use case is to turn a rule on or off based on a constant somewhere, or just by directly editing the code during a debugging session. --- 14.11.1: The compatibility rules are specified as "for all" rules, which means you can do some surprising things when they're vacuously true... double d = 23.0; switch (d) { default -> System.out.println("ok"); } // no error (This is an "enhanced switch", so requires a default to be exhaustive.) This seems okay to me?Java doesn't prevent all stupid programs?but worth a sanity check. And we should be careful not to make assertions that claim such switches can't be written. --- 14.11.1: A couple of dominance design notes: - Claiming that a guarded type pattern dominates a constant seems overly-nannying to me. Usually, these sorts of errors occur because the compiler can *prove* you've done something stupid (e.g., "this cast will always fail"). Here, the compiler is assuming the switch rule is stupid unless the programmer can prove it's not, to the compiler's satisfaction. And the compiler isn't very smart! Suggestion: just leave guarded type patterns out, they don't dominate anything else. - It's weird that 'null' is the one case constant that can't come after a 'default', given that 'null' is the one value that can't be handled by a 'default'. :-) Since we have to allow other case constants after 'default' anyway, I think it's best to allow 'null' as one more. ("default dominates everything except constants" is a rule I can remember.) (Also note?and I think this is good?that the dominance rules for 'default' don't care whether this is an "enhanced switch" or not. The fewer things that depend on enhanced vs. traditional, the better.) --- 14.11.1: I don't think we prevent this scenario: switch (obj) { case String s: case Object o: } The rules about duplicate pattern labels apply to "a statement is labeled...". There's no labeled statement here. I think we should probably prohibit this, for the same code hygiene reason that we prohibit it before a statement group that doesn't try to use any of the variables. (This would not be a legal switch expression, because switch expressions can't fall out. But enhanced switch statements can, as long as they're exhaustive.) --- 14.11.2: Design suggestion: rename "enhanced switch" to "pattern switch", define it as only those switches that make use of patterns, and don't worry about the remaining "switch with new features" corner cases. It's just such an important concept that I think there's a benefit to making the distinction really clean and obvious. E.g., asking someone new to Java to memorize the ad hoc set of types that don't demand exhaustiveness seems unhelpfully complicated. (Corner cases I'm thinking about: want to use a null constant but not be exhaustive? Fine. Want to have an Object input type but an empty switch body? Pointless, but fine. Etc.) --- 14.11.3, 15.28.2: Opinionated take: we're continuing to throw ICCE when an unmatched enum constant slips through a switch, because it would be a (behaviorally) incompatible change to throw something else. Meanwhile, unmatched sealed classes get a MatchException. I think there are a tiny number of people who would notice if we changed from ICCE to MatchException for enums too, and a lot more people who will have to cope with the historical baggage going forward if we don't. We should just standardize on MatchException. Further complication: as written, a sealed-interface switch that doesn't mention any enum constants will still throw if the value that gets through happens to be an enum constant. switch (sealedThing) { case Foo f -> {} case Bar b -> {} // an enum class gets added as a permitted subclass: ICCE // a regular class gets added as a permitted subclass: ME } We could specify this differently, but I'm not sure there's a bright line that will be intuitive. --- 14.14.2: I'm sure there's been some discussion about this already, but the use of MatchException for nulls in 'for' loops but NPE for nulls in switches also seems like a sad historical wart. What's wrong with an NPE from a 'for' loop? --- 14.30.1: Parenthesized patterns are no fun for a spec writer. :-) Are they actually useful? I'm not sure I've seen an example demonstrating what they're for. (The JEP only talks about them abstractly.) Followup: would it make sense for a 'for' loop to permit a parenthesized pattern? --- 14.30.1, 14.30.2: I'm not sold on *any patterns*, *resolved patterns*, and *executable switch blocks*. The semantics are fine?some type patterns will match null, others will not. But it seems to me that this can be a property of the type pattern, one of many properties of programs that we determine at compile time. No need to frame it as rewriting a program into something else. (Compare the handling of the '+' operator. We don't rewrite to a non-denotable "concatenation expression".) Concretely: - The pattern matching runtime rules can just say "the null reference matches a type pattern if the type pattern is unconditional". - We can make it a little more clear that a type pattern is determined to be unconditional, or not, based on its context-dependent match type (is that what we call it?) For a *compiler*, it will be useful to come up with an encoding that preserves the compile-time "unconditional" property in bytecode. But that's a compiler problem, not something JLS needs to comment on. --- 14.30.3: To my ear (maybe this is an American thing?), "applicable at" sounds wrong, and it would read more naturally to say "applicable for" or "applicable to". Same for "unconditional at". --- 14.30.3: A record pattern can't match null, but for the purpose of dominance, it's not clear to me why a record pattern can't be considered unconditional, and thus dominate the equivalent type pattern or a 'default'. switch (point) { case Point(int x, int y) -> {} case Point p -> {} // unreachable default -> {} // unreachable } From forax at univ-mlv.fr Fri Oct 28 07:00:33 2022 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 28 Oct 2022 09:00:33 +0200 (CEST) Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> <2118717224.31682166.1666475526444.JavaMail.zimbra@u-pem.fr> <15EB4E60-49A5-4DDB-A677-5F55F9BB8683@oracle.com> <868743552.35478762.1666908198171.JavaMail.zimbra@u-pem.fr> <4BC33B28-5A61-4FFE-9EB7-039BCD98E91A@oracle.com> <1904405125.35479800.1666909505095.JavaMail.zimbra@u-pem.fr> Message-ID: <1879618215.35744842.1666940433107.JavaMail.zimbra@u-pem.fr> ----- Original Message ----- > From: "daniel smith" > To: "Remi Forax" > Cc: "Brian Goetz" , "Gavin Bierman" , "amber-spec-experts" > > Sent: Friday, October 28, 2022 12:58:56 AM > Subject: Re: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record > Patterns (JEP 432) now available >> On Oct 27, 2022, at 3:25 PM, forax at univ-mlv.fr wrote: >> >>>> Either i can use >>>> case Point p -> >>>> >>>> or >>>> case Point(var x, var y) -> >>>> >>>> but this is not valid anymore >>>> case Point(var x, var y) p -> ... >>> >>> Can always do this, right? >>> >>> case Point p where p instanceof Point(var x, var y) -> >> >> Gavin already proposed that, in France, we have a sentence for that >> Why do it the easy way when you can do it the hard way ? [1] > > I dunno, I guess I don't really understand the motivation. > > If you do 'case Point(var x, var y)', that solves your problem: won't compile if > a new component comes along. > > If you do 'case Point p', it *will* compile, of course, just like if you declare > a field of that type. It won't check the arity, because we don't check > properties of classes based on a random mention of them. > > I guess you're really looking for something like 'case Point(#2#) p', some > compact way to assert that the class you're talking about still has 2 record > components. > > But... this is such a niche-sounding concern to me, I don't see why *records* > are the one kind of class in Java for which such a feature would be important. Because one of the use case of record is to represent data and data change when requirements changed. Here is a simple example written by one of my TA, where you want to compute something depending on some component of the record and the record as a whole public int maxTravelTime() { return passengers.stream() .mapToInt(p -> switch(p) { case Tourist(String name, boolean vip, int credits) tourist -> tourist.maxHibernation() + credits / 1_000; case Crew(String name, int age, Rank rank) -> 60 - age; }).max().orElse(0); } In the case Tourist, the result depends both on some methods of the record (maxHibernation()) and the value of some component (credits). This code currently complies with Java 19, but it will not with Java 20. > > In any case, good news?even though we don't have this dedicated feature, there > are various ways you can mention the record class in a record pattern and get > the compiler to make this check. Just costs a few more characters. ???? > >> moreover this pattern+where is not exhaustive on Point. > > Ah, that's interesting. I think we need to think more about what features or > idioms we'll recommend for people to do assignment-like decomposition. I'm not > sure that's settled yet. > > If we arrive at 'RecordType instanceof RecordPattern' as a common decomposition > idiom, then I think it would be reasonable to expect the analysis of guards > (and probably DA/DU, etc....) to special-case this and realize it will always > be true. I would prefer to keep the story about where the analysis is done or not as simple as possible, currently it's fairly simple, you have analysis inside the pattern part but not inside the where part. That's why constant pattern is one of the next items on the roadmap BTW, because case Person(String name, boolean vip) when vip -> is not semantically equivalent to case Person(String name, eq true) -> // or whatever the syntax we choose for constant patterns. R?mi From gavin.bierman at oracle.com Fri Oct 28 21:07:38 2022 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 28 Oct 2022 21:07:38 +0000 Subject: Draft Spec for Fourth Preview of Pattern Matching for Switch (JEP 433) and Second Preview of Record Patterns (JEP 432) now available In-Reply-To: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> References: <3E794F9B-EDB3-4CE3-B43E-29AA709FF8BF@oracle.com> Message-ID: The draft spec has been updated with type inference for record patterns. For now - following some feedback - we?re pausing the thought of adding type inference for type patterns using a diamond form. [I?ll post separately about this shortly - we should discuss this possibility further.] https://cr.openjdk.java.net/~gbierman/jep432+433/latest Comments on this draft are welcome. I have some other edits queued up; I?ll email the list when they have been applied. (The URL above will always point to the most recent build.) Thanks, Gavin On 18 Oct 2022, at 23:22, Gavin Bierman > wrote: Dear experts: The first draft of a joint spec covering JEP 433 (Fourth Preview of Pattern Matching for switch) and JEP 432 (Record Patterns) is available at: https://cr.openjdk.java.net/~gbierman/jep432+433/latest This covers all the changes for these JEPs *apart* from the proposal to infer type arguments in patterns. This will be added and announced shortly. Comments welcomed! Gavin -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Oct 25 09:59:34 2022 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 25 Oct 2022 09:59:34 -0000 Subject: [deconstruction-patterns] Unconditional patterns and null (again) Message-ID: Hello! I've just discovered that recent builds of JDK 19 and JDK 20 accept this code, which fails with NPE at runtime: class Test { record R(int x) {} static void test(R r) { switch (r) { case R(int x) -> System.out.println("matched"); default -> System.out.println("not matched"); } } public static void main(String[] args) { test(null); } } If I remember correctly, it was rejected previously with error similar to this (you can emulate the error replacing `R(int x)` with `R r1` > Test.java:7: error: switch has both an unconditional pattern and a default label I see that in a recent spec draft [1] it's said that: > Note that record patterns are not unconditional at any type because the null reference does not match any record pattern. Which makes sense. And it looks like that compiler follows the spec. However, we all know that the default branch is not reachable in this switch, so we effectively accept unreachable code. Is this intended or should be changed somehow? With best regards, Tagir Valeev [1] https://docs.oracle.com/javase/specs/jls/se19/preview/specs/patterns-switch-record-patterns-jls.html#jls-14.30.3 From brian.goetz at oracle.com Tue Oct 25 15:08:57 2022 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 25 Oct 2022 15:08:57 -0000 Subject: [deconstruction-patterns] Unconditional patterns and null (again) In-Reply-To: References: Message-ID: There is a (deliberate) gap between an unconditional pattern and a set of patterns that is exhaustive at a type.? `R r` is unconditional on R, but `R(var x)` is not, because null -- but the set of patterns { R(var x) } is _exhaustive_ at R.? Null is in the _remainder_ of { R(var x) } on R.? So the spec says what it means and the compiler is implementing that, but ... we are not necessarily doing the user a favor here. The question is how much analysis the compiler should do to catch errors surrounding remainder.? Because a switch without a `case null` already rejects null, we could conclude that the null covered by switch and the values covered by { R(var x) } together cover everything, and therefore the default is dead.? But with the spec we have, since we don't compute the remainder explicitly, this would likely have to be a special case for "can we prove that null is the only value in the remainder set". Note that a similar analysis would be needed to make this exhaustive when we get to constant patterns: ??? Boolean b = ... ??? switch (b) { ??????? case TRUE: ... ??????? case FALSE: ... ??? } For now, I think it would be prudent for the compiler to issue a warning here, and we'll take a note to look into tightening the analysis for "null \union everything but null" cases. On 10/25/2022 5:59 AM, Tagir Valeev wrote: > Hello! > > I've just discovered that recent builds of JDK 19 and JDK 20 accept > this code, which fails with NPE at runtime: > > class Test { > record R(int x) {} > > static void test(R r) { > switch (r) { > case R(int x) -> System.out.println("matched"); > default -> System.out.println("not matched"); > } > } > > public static void main(String[] args) { > test(null); > } > } > > If I remember correctly, it was rejected previously with error similar > to this (you can emulate the error replacing `R(int x)` with `R r1` >> Test.java:7: error: switch has both an unconditional pattern and a default label > I see that in a recent spec draft [1] it's said that: >> Note that record patterns are not unconditional at any type because the null reference does not match any record pattern. > Which makes sense. And it looks like that compiler follows the spec. > However, we all know that the default branch is not reachable in this > switch, so we effectively accept unreachable code. Is this intended or > should be changed somehow? > > With best regards, > Tagir Valeev > > [1] https://docs.oracle.com/javase/specs/jls/se19/preview/specs/patterns-switch-record-patterns-jls.html#jls-14.30.3