From forax at univ-mlv.fr Sat Apr 1 06:43:40 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sat, 1 Apr 2023 08:43:40 +0200 (CEST) Subject: Feedback: String Templates (JEP 430) In-Reply-To: References: <66693205-3ae6-cbc8-59d9-8d2af6a42891@oracle.com> <740055836.25615197.1680291031692.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <658037114.25922276.1680331420226.JavaMail.zimbra@univ-eiffel.fr> > From: "John Rose" > To: "Remi Forax" > Cc: "Reinier Zwitserloot" , "Brian Goetz" > , "amber-dev" > Sent: Friday, March 31, 2023 10:23:20 PM > Subject: Re: Feedback: String Templates (JEP 430) > On 31 Mar 2023, at 12:30, Remi Forax wrote: >> ? >> I agree that interpolate() is too easy to misuse but at the same time, it's a >> useful primitive. > +1 >> I wonder if the solution is to add an escape function, a function that takes an >> Object and returns an Object that should escape the values to interpolate. >> Something like >> public String StringTemplate.interpolate(UnaryOperator escapeFunction) { >> ... } >> By asking for an escape function, we are making the API safer to use. > But the workaround is saying interpolate(x->x) and grumbling about ceremony. > That workaround doesn?t get much closer to exposing the root problem. Also, the > unary operator, if we were to do this functionally as suggested, needs to ?see? > more context about each value that it would be interpolated. > It seems to me that part of the problem here is the responsibility for escaping > is on the wrong side of the fence, with interpolate as presently discussed. > When offered a function whose contract knows only about string joining, the > party producing stringy bits to join into a correct DSL statement is burdened > with responsibility of figuring out how and when to escape them, and in their > various DSL-specific contexts. > But surely that knowledge belongs more exactly to the ST processor, not to the > supplier of interpolation values. Some languages have context-dependent quoting > rules. Note that Remi?s suggested unary function doesn?t see the context. It > could be given context as interpolate((x,c)->?) , and that begs a very good > question about the type of c. What I?m saying is that c is not something the > supplier of interpolation values should be forced to worry about. > For example in SQL values are quoted with a single quote but names are quoted > with a different kind of quote, often vendor-specific; yuck. Fighting against > code-injection might require getting the correct contextual flavor of quote in > each case, if not for SQL then for more complex templated notations. It?s lucky > for SQL that textually doubling ' to '' will cover most use cases, regardless > of context, but that?s just luck. JSON has distinct kinds of values, which > would require distinct tactics for validation and/or quoting; you need > quote-escapes for string bodies and field names but not for numbers. If you > were trying to do Java templates you?d want to know the contextual difference > between char and string literals. > Generally speaking, getting the quoting right is not the direct responsibility > directly of people supplying values to interpolate, but rather the > responsibility of the party weaving together a (correct) template (SQL or JSON > or ?). Asking the value-supplier to shoulder the burden of correct quoting > requires a mix of two kinds of expertise (business logic and query syntax), > which is how bugs happen. > I think I would prefer to see a formulation of interpolate which would require > users to take apart the ST processor, lower it into a plain-string-cat template > processor, and then run a natively string-cat-ing format operation on it; after > that it can be lifted back to its DSL, with fingers crossed that we got avoided > bad injections. But I admit I haven?t figured out the details, so that?s just a > vague suggestion? > What I hope is clear is my point about separating concerns, between knowing how > and when to escape a value in a particular place , and coming up with a set of > interpolation values for those places. It?s rooted in the distinction between > an envelope and its contents. Quoting (and validation) is something > envelope-specific. Contents are usually specific to some completely unrelated > domain of business logic. Unless API users are helped to separate those > concerns, there will be confusion, exploitable in attacks. Thanks John, A string template processor takes a template (a List), an arguments descriptor (a MethodType) and the values (a List) and produce a result List -> MethodType -> List -> result The current implementations provides two API points, TS.Processor.Linkage groups the arguments that way (List, MethodType) -> List -> result while TS.Processor groups the arguments into TemplatedString as an intermediary object TemplatedString (List, MethodType, List) -> result The question is what primitive to provide to a TS.Processor that uses string interpolation internally (TS.interpolate()). A SQL TS.Processor, I hope, will use PreparedStatement internally and not rely on string interpolation, so will not call TS.interpolate(), but anyway your point about separating the processing of the fragments and the values holds. It's not a secret that i do not like the interface TS.Processor because the incentive is wrong, it's hard to say at the same time, beware as an implementer of a processor you have to be cautious about injections so you have to separate the way you deal with fragments and values (to escape them correctly) and at the same time provide an API that regroups the fragments and the values into the same object. It seems that a better API should be to not provide TS.interpolate as an instance method and change the static methods to takes both the fragments and the values and hopes that the template processor implementer uses that method call with care. I think it's better to provide a method that let everyone mess with string concatenation because at least you have an easy entrypoint in term of security auditing. --- More about why i do not like TS.Processor API, "a particular place", i.e. the call site is something available using the TS.Processor.Linkage API but sadly not available using the TS.Processor API so all processor implementers will re-invent that notion using caching (see Reinier part of the message about JOOQL + cache). I think that making the API to define a template processor too easy is a mistake. The sadistic part of me also think that the TS.Processor API entrypoint should not exist and that only TS.Processor.Linkage should exist, because TS.Processor.Linkage returns a MethodHandle and if you are able to understand how MethodHandle works, there is also a good chance that you know how to deal with injections. I would prefer to live in a world with fewer template processors because the bar to create one is higher than live in a world with a lot of template processors with half of them being wrong. > ? John R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sat Apr 1 06:43:54 2023 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Sat, 1 Apr 2023 08:43:54 +0200 (CEST) Subject: Feedback: String Templates (JEP 430) In-Reply-To: <4611E876-8470-44B4-B182-F226F662D482@oracle.com> References: <66693205-3ae6-cbc8-59d9-8d2af6a42891@oracle.com> <740055836.25615197.1680291031692.JavaMail.zimbra@univ-eiffel.fr> <4611E876-8470-44B4-B182-F226F662D482@oracle.com> Message-ID: <1395319286.25922287.1680331434938.JavaMail.zimbra@univ-eiffel.fr> > From: "John Rose" > To: "Remi Forax" > Cc: "Reinier Zwitserloot" , "Brian Goetz" > , "amber-dev" > Sent: Friday, March 31, 2023 10:28:00 PM > Subject: Re: Feedback: String Templates (JEP 430) > P.S. As a for-instance, a good way to separate the internal concerns of envelope > logic from the concerns of a payload-providing client is to make the low-level, > expert-only string interpolation function be protected in the abstract > DSL-implementor class. Then the clients only use DSL-specific API points, but > internally the string assembly happens smoothly. I guess that?s not an option > with interfaces, but it is one of the classic ways to avoid the confusion > between envelope logic and payload logic ClassLoader.defineClass is an entrypoint like that, and in practice a lot of people myself included were (before Lookup.defineClass() was added) adding a public method inside the classloader implementation that delegates to the protected method. So while this design may help a little, it is not fundamentally different from a public API. R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From pholder at gmail.com Sat Apr 1 04:08:02 2023 From: pholder at gmail.com (P Holder) Date: Sat, 1 Apr 2023 00:08:02 -0400 Subject: Other ways to use destructuring Message-ID: In thinking about record destructuring it occurs to me it could be used in more places than currently proposed. One example would be in a function call: record HumanName(firstName: String, middleName: String, lastName: String) {} HumanName humanName = new HumanName(?Alice?, ?Bateman?, ?Chandler?); String getFormattedName(humanName: HumanName(firstName, _, lastName)) { return lastName + ?, ? + firstName; } final String fmtName = getFormattedName(humanName); another could be in a for loop final Set humans = new HashSet<>(); humans.add(humanName); for (final humanName: HumanName(firstName, _, lastName): humans) { processFirstName(firstName); processLastName(lastName); ... } or indeed in any body with humanName as HumanName(firstName, _, lastName) { processFirstName(firstName); processLastName(lastName); ... } -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Apr 3 09:14:16 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Apr 2023 05:14:16 -0400 Subject: Other ways to use destructuring Message-ID: Hello P Holder, Thank you for posting! I'm not part of the Amber Dev Team, just a commenter, but I have a few thoughts. > In thinking about record destructuring it occurs to > me it could be used in more places than currently > proposed. I'll start off by making the assumption that all of these examples you then provided are things that are currently not possible in Java 20. I have been playing with destructuring a lot lately, but I might have missed a few things. So, putting this disclaimer up front. Working on that assumption, I see a lot of value in what you are describing, but I fear that there is a significant cost that is not being acknowledged in your proposal. I'll go through each one. > One example would be in a function call: > > record HumanName(firstName: String, middleName: String, lastName: String) {} > > HumanName humanName = new HumanName(?Alice?, ?Bateman?, ?Chandler?); > > String getFormattedName(humanName: HumanName(firstName, _, lastName)) > { > > return lastName + ?, ? + firstName; > > } > > final String fmtName = getFormattedName(humanName); You have brought an implementation detail of the class into the method signature of getFormattedName. The purpose of a method signature is to define a method's name, its parameters, and its return type. To add things like this destructuring to the signature adds a lot of complexity. Complexity can be acceptable, but there needs to be a good reason for it being there. I can definitely see some good reasons. Looking at the above signature, it is clear that a null HumanName will end up in some failure state for this method. Exception probably. It's also clear that middleName isn't required, so it allows me to focus my validation efforts on the fields required. This proposal definitely provides value. However, I don't think that the value outweighs the costs here. Most of the value I described could be achieved by both looking at the contents of the method you are calling and reading its preconditions. Yes, sometimes, the method we are looking at has no documentation and has already been compiled with no source code available. But even then, this feels like a poor solution to deal with that problem. After all, not all preconditions can be (easily or reasonably) described through deconstruction patterns. And even if they could be, you still end up with a constraint that only exists at run time. None of those preconditions exist at compile time. But maybe I am not seeing it. Could you go into more details about the benefits that adding deconstruction patterns in the method header provides us? > another could be in a for loop > > final Set humans = new HashSet<>(); > > humans.add(humanName); > > for (final humanName: HumanName(firstName, _, lastName): humans) > > { > > processFirstName(firstName); > > processLastName(lastName); > > ... > > } This functionality is actually already being considered. In fact, it was briefly previewed before they pulled it back. It's written in JEP 440 that they took it out from the record patterns JEP, so that it can be delivered separately later on. So, it is on its way, just not here now. Here is a link to the JEP 440 itself -- https://openjdk.org/jeps/440 I would also click around and view the other JEP's involving record patterns -- https://openjdk.org/jeps/0 > or indeed in any body > > with humanName as HumanName(firstName, _, lastName) > > { > > processFirstName(firstName); > > processLastName(lastName); > > ... > > } This too is being considered. Here is a youtube video that goes into what a possible implementation of this might look like -- https://youtu.be/L6BS2F0dWXQ?t=4485 Thank you for reaching out! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From duncan.macgregor at servicenow.com Mon Apr 3 10:41:59 2023 From: duncan.macgregor at servicenow.com (Duncan MacGregor) Date: Mon, 3 Apr 2023 10:41:59 +0000 Subject: Feedback: String Templates (JEP 430) In-Reply-To: <23c6229c-3e85-9be6-4d2e-7ded42a4a798@oracle.com> References: <66693205-3ae6-cbc8-59d9-8d2af6a42891@oracle.com> <23c6229c-3e85-9be6-4d2e-7ded42a4a798@oracle.com> Message-ID: While I agree that compile time type checking and constants are a separate topic, string templates, and especially the template formatter, seem like a reasonable entry point to think about them as they rely on a new bit of syntax and are being compiled to something special (an invoke dynamic based callsite whose bootstrap takes the fragments and the method type). The problem I see at the moment is that it?s not clear to me that the type information remains available to the processor in a way that would make for compile time type checking to be added later, and it would be a shame if we cut this path off now. The ProcessorLinkage interface feels like it might be closer to path allowing this, but I see that it?s sealed at the moment. It would be sad if we can?t leverage a new way of string formatting to introduce compile time type checking of those format strings when it feels like we?re really close to being on a path that would enable it in a natural way. Duncan. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Apr 3 12:22:55 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 3 Apr 2023 08:22:55 -0400 Subject: Other ways to use destructuring In-Reply-To: References: Message-ID: > In thinking about record destructuring it occurs to me it could be > used in more places than currently proposed. > Yes, these are what we've been calling "imperative destructuring", where an exhaustive pattern can be used in a non-conditional context.? There was a hint of this in the experimental use of record patterns in foreach loops, we've talked about a `let` or `match` statement a fair bit, and have briefly discussed using destructuring in method headers (though we are pretty negative on that one right now.) We're being deliberately conservative about this; right now it feels like it's in the "because we can" stage, and that's where mistakes come from.? We will revisit when we nail down the declaration story for matchers, since these are likely to come back into play when we address matcher implementations delegating to other matchers (analogous to "super" calls in constructors.) > > One example would be in a function call: > > > record HumanName(firstName: String, middleName: String, lastName: > String) {} > > HumanName humanName = new HumanName(?Alice?, ?Bateman?, ?Chandler?); > > > String getFormattedName(humanName: HumanName(firstName, _, lastName)) > > { > > ?? return lastName + ?, ? + firstName; > > } > > > final String fmtName = getFormattedName(humanName); > > > > another could be in a for loop > > > final Set humans = new HashSet<>(); > > humans.add(humanName); > > for (final humanName: HumanName(firstName, _, lastName): humans) > > { > > processFirstName(firstName); > > processLastName(lastName); > > ?? ... > > } > > > or indeed in any body > > > with humanName as HumanName(firstName, _, lastName) > > { > > processFirstName(firstName); > > processLastName(lastName); > > ?? ... > > } > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Apr 3 12:33:58 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 3 Apr 2023 08:33:58 -0400 Subject: Feedback: String Templates (JEP 430) In-Reply-To: References: <66693205-3ae6-cbc8-59d9-8d2af6a42891@oracle.com> <23c6229c-3e85-9be6-4d2e-7ded42a4a798@oracle.com> Message-ID: <5d1963f4-eff4-11c4-73cd-b14f4c13ddd8@oracle.com> If you pull on this string a little more, you realize there is very little string left before you get to the knot labeled "compiler plugins".? In order to get compile-time type checking for arbitrary template processors, the template processor class must be available at compile time, and the compiler must be willing to invoke it during compilation.? While I realize there are lots of folks who think that would be a great thing, that's pretty far from the goal of this work. For processors like FMT, since they are specified in the JDK, IDEs can do much of the work here, and it is conceivable `javac` can have lint warnings that understand the format specifier. For arbitrary processors, annotation processors can add additional ad-hoc type checking. On 4/3/2023 6:41 AM, Duncan MacGregor wrote: > > While I agree that compile time type checking and constants are a > separate topic, string templates, and especially the template > formatter, seem like a reasonable entry point to think about them as > they rely on a new bit of syntax and are being compiled to something > special (an invoke dynamic based callsite whose bootstrap takes the > fragments and the method type). The problem I see at the moment is > that it?s not clear to me that the type information remains available > to the processor in a way that would make for compile time type > checking to be added later, and it would be a shame if we cut this > path off now. The ProcessorLinkage interface feels like it might be > closer to path allowing this, but I see that it?s sealed at the moment. > > It would be sad if we can?t leverage a new way of string formatting to > introduce compile time type checking of those format strings when it > feels like we?re really close to being on a path that would enable it > in a natural way. > > Duncan. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Apr 3 13:35:41 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 3 Apr 2023 15:35:41 +0200 (CEST) Subject: Feedback: String Templates (JEP 430) In-Reply-To: <5d1963f4-eff4-11c4-73cd-b14f4c13ddd8@oracle.com> References: <66693205-3ae6-cbc8-59d9-8d2af6a42891@oracle.com> <23c6229c-3e85-9be6-4d2e-7ded42a4a798@oracle.com> <5d1963f4-eff4-11c4-73cd-b14f4c13ddd8@oracle.com> Message-ID: <1935245132.27330939.1680528941715.JavaMail.zimbra@univ-eiffel.fr> > From: "Brian Goetz" > To: "Duncan MacGregor" , "amber-dev" > > Sent: Monday, April 3, 2023 2:33:58 PM > Subject: Re: Feedback: String Templates (JEP 430) > If you pull on this string a little more, you realize there is very little > string left before you get to the knot labeled "compiler plugins". In order to > get compile-time type checking for arbitrary template processors, the template > processor class must be available at compile time, and the compiler must be > willing to invoke it during compilation. While I realize there are lots of > folks who think that would be a great thing, that's pretty far from the goal of > this work. > For processors like FMT, since they are specified in the JDK, IDEs can do much > of the work here, and it is conceivable `javac` can have lint warnings that > understand the format specifier. > For arbitrary processors, annotation processors can add additional ad-hoc type > checking. The typechecking + call invokedynamic can also be implemented as a Leyden Condenser (once we have them). In that case, the typechecking is delayed until the image creation. I believe that if the template processor is implemented using a lazy static final [1] (so it's initialization does not depend of the execution of the static block), we can come with a condenser generic enough to work with all template processors. regards, R?mi [1] https://bugs.openjdk.org/browse/JDK-8209964 > On 4/3/2023 6:41 AM, Duncan MacGregor wrote: >> While I agree that compile time type checking and constants are a separate >> topic, string templates, and especially the template formatter, seem like a >> reasonable entry point to think about them as they rely on a new bit of syntax >> and are being compiled to something special (an invoke dynamic based callsite >> whose bootstrap takes the fragments and the method type). The problem I see at >> the moment is that it?s not clear to me that the type information remains >> available to the processor in a way that would make for compile time type >> checking to be added later, and it would be a shame if we cut this path off >> now. The ProcessorLinkage interface feels like it might be closer to path >> allowing this, but I see that it?s sealed at the moment. >> It would be sad if we can?t leverage a new way of string formatting to introduce >> compile time type checking of those format strings when it feels like we?re >> really close to being on a path that would enable it in a natural way. >> Duncan. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Apr 3 14:06:21 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 3 Apr 2023 10:06:21 -0400 Subject: Some thoughts and suggestions regarding last month's post on Deconstruction Patterns Message-ID: Hello Amber Dev Team, Here is the post I am referencing -- https://mail.openjdk.org/pipermail/amber-spec-experts/2023-March/003766.html The link above paints a pretty beautiful picture for the future of object deconstruction. We now have a solid idea of the semantics behind how deconstruction can work, and I think it flows really nicely. In my code, all I need to do is identify a common form of deconstruction I perform in my code base, abstract that out to its own deconstructor, then simply call that deconstructor when needed to simplify object decomposition. And since we know that patterns can compose, we can easily see how this might fit in with nested patterns. However, looking at this gives me some pause too, because that means that the code is in our hands to write, for better or for worse. There seems to be no form of constraint placed on these custom deconstruction pattern - just that you have to assign a value to all of the "out-parameters". My fear is with how open ended and flexible the concept of custom deconstruction is. Aside from the above constraint, these deconstructors seem to function just like methods. And one very common source of bugs in the code we all write is when a method does something wildly different than what you would expect. A method says getField(), but it modifies state inside the method call or throws an exception for some likely incorrect reason. Now, the above link does a good job of showing how easy and simple it is to write and read a deconstructor. So I'm not concerned about it on the small scale. The problem is that deconstructors were meant to be composed. They were meant to be nested. It's that trait that makes this feature so worth it. You can deconstruct complex objects down to just their essentials in an expressive and intuitive way. And therefore, regardless of how small the chance of a bug is, that chance gets multiplied, not just for each level of decomposition, but for each deconstructor that you write. After all, it's not just about having the tool be correct, but using it correctly. So the chance of failure, regardless of how small, grows exponentially. All that said, I have some ideas on how to severely limit that rate of growth. One suggestion would be the ability to create deconstructors that delegate some of their work to other deconstructors. That way, you can significantly limit the first point of failure -- writing the proper deconstructor in the first place. This limits the attack surface (in exchange for creating greater downstream impact if it does fail) by avoiding duplication and just delegating. Another suggestion would be to create some concept of a "final" for the fields that we are assigning stuff to. Being able to force myself to assign something a value, but also only assign it once, is a powerful construct that already helps me when writing normal code, so I know that it can really help to avoid making some of the same mistakes here and limit the errors in my decomposition. Yet another suggestion would be something a little more controversial (not to mention that this ship may have already sailed) -- I'd like the user of the deconstruction pattern to be able to opt-in to forcing their binding variables to CONTAIN the same identifiers that were used at the deconstructing method's signature. Being able to know the field names allows me to be sure that I am grabbing the right field from my pattern match. This one is definitely the most guard-rail-y and least java-like of the bunch, but I feel like deconstruction deserves the extra guard rails. We consider it a code smell to make a method that has many parameters, but the entire point of deconstruction is to get all the necessary fields out as efficiently and simply as possible. That ends up feeling like that same code smell once you get to any sort of depth. However, I think we can bypass the risk associated with the code smell by allowing people to opt in to this naming concept. Then, the number of parameters shouldn't be a threat anymore since they are all named, and thus, clearly defined. Again, opt in. Honestly, even the first 2 alone would still do a lot of good. But these are 3 suggestions I had that might help people use this feature properly. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From krka at spotify.com Tue Apr 4 09:28:36 2023 From: krka at spotify.com (Kristofer Karlsson) Date: Tue, 4 Apr 2023 11:28:36 +0200 Subject: Language feature to support Fluent Syntax for Static Method Calls In-Reply-To: References: <00A71EBF-03AE-42FD-8655-F7FF0E6B9704@oracle.com> <01353081-4d52-9f69-70c1-5fbaf7139d55@oracle.com> <1fa784a8-5fb4-e49e-0eb3-aec41719dfb8@gmail.com> Message-ID: Following up on the part about the String.transform method that was added with JEP 326 that R?mi mentioned, is this something worth considering adding for other classes and interfaces? Specially, I think these ones would be very useful to have: interface CompletionStage { default R transform(Function,R> f) { return f.apply(this); } } class CompletableFuture { default R transform(Function,R> f) { return f.apply(this); } } class Optional { default R transform(Function,R> f) { return f.apply(this); } } I could not find any old discussions as to why this would be a useful thing to have for strings instead of just doing a regular method call: The JEP shows: String stripped = ` | The content of | the string `.transform(MyClass::stripMargin); but it could also have been written as: String stripped = MyClass.stripMargin( ` | The content of | the string `); so I think it is already a very similar use case to chaining method calls related to futures. Best regards Kristofer On Wed, Mar 29, 2023 at 1:43?PM Kristofer Karlsson wrote: > > Thank you all for your well thought out replies! > > I've taken some time to think everything through and made some mental > adjustments: > > With regards to streams, I definitely agree that .toSet() would not be a > significant improvement over .collect(Collectors.toSet()). That was a > bad example. > Also for controlling parallel streams, since that is typically done at the start > of setting up a stream, it will be a limited problem in practice. > I can't really come up with any other reasonable examples for streams, > so perhaps > that train of thought should be dropped. > > For CompletableFuture I think there are definitely some missing methods that > should be added. It is somewhat surprising given that the API is in some ways > very big where many methods do almost exactly the same thing. > > Some of that has been fixed already (thinking primarily of > exceptionallyCompose), > but isn't that itself somewhat of a signal that APIs can be imperfect and may > sometimes need to be fixed. I imagine this will not be the last time > that happens. > > So, I think the problem is still a problem - but do not have good insights into > the size of the problem. For me it is mostly an inconvenience that leads to > more verbose code, but I've seen other people at my work do the entirely wrong > thing because the right thing to do was not easily available to them in the API. > The impact of such mistakes comes in the form of performance issues > (blocking threads in an environment that relies on asynchronous work > with futures). > > Thinking about the solutions to the problem, there are many options that do not > require any changes. Let's look at some of them, using > exceptionallyCompose as an example: > > If this method exists (i.e. we're in Java 9+) we can do: > > return someRemoteCall() // step 1 > .thenApply(x -> x + x) // step 2 > .thenCompose(x -> otherRemoteCall(x)) // step 3 > .exceptionallyCompose(e -> retryRemoteCall(e)) // step 4 > .thenApply(x -> transformResponse(x)) // step 5 > > In Java 8 we can implement it manually roughly like this: > > public static CompletableFuture exceptionallyCompose( > CompletableFuture future, Function CompletionStage func) { > return future > .thenApply(CompletableFuture::completedFuture) > .exceptionally(func) > .thenCompose(Function.identity()); > } > > Then we have many different ways we could write the code: > > var f1 = someRemoteCall() // step 1 > .thenApply(x -> x + x) // step 2 > .thenCompose(x -> otherRemoteCall(x)) // step 3 > return exceptionallyCompose(f1, e -> retryRemoteCall(e)) // step 4 > .thenApply(x -> transformResponse(x)) // step 5 > > Pro: method ordering matches the source code ordering > Con: we need to introduce a variable for this. If this was in a lambda, > we would need to use blocks. > > Or we could write it like this: > > return exceptionallyCompose( // step 4 > someRemoteCall() // step 1 > .thenApply(x -> x + x) // step 2 > .thenCompose(x -> otherRemoteCall(x)), // step 3 > e -> retryRemoteCall(e)) > .thenApply(x -> transformResponse(x)) // step 5 > > The only problem with this is that the method ordering is less intuitive in the > source code. Also, the parameter for exceptionallyCompose is 4 lines > away from the > method itself. > > Another alternative is to create a wrapper interface and use it like this: > > return wrapAsFuture2(someRemoteCall()) // step 1 > .thenApply(x -> x + x) // step 2 > .thenCompose(x -> otherRemoteCall(x)) // step 3 > .exceptionallyCompose(e -> retryRemoteCall(e)) // step 4 > .thenApply(x -> transformResponse(x)) // step 5 > .toCompletableFuture() > > Here the method order is preserved, but we need to create a full wrapper of > the interface which is an extra bit of boilerplate code. > > I did not know about the (new) transform method in String - but that's also a > useful pattern! If that was added to the most important classes and interfaces > in the JDK, that would solve a lot of the concrete issues I think. > > With that, the code would look like: > > return someRemoteCall() // step 1 > .thenApply(x -> x + x) // step 2 > .thenCompose(x -> otherRemoteCall(x)) // step 3 > .transform(f -> exceptionallyCompose(f, e -> retryRemoteCall(e))) // step 4 > .thenApply(x -> transformResponse(x)) // step 5 > > which is not too bad. > > If we are talking about language features, I think two other approaches would be > some postfix expression extension operator (previously referred to as thrush). > With something like that, the code would look like: > > return someRemoteCall() // step 1 > .thenApply(x -> x + x) // step 2 > .thenCompose(x -> otherRemoteCall(x)) // step 3 > OP f -> exceptionallyCompose(f, e -> retryRemoteCall(e)) // step 4 > .thenApply(x -> transformResponse(x)) // step 5 > > Finally, the approach with alternative syntax for static method calls. > If we are concerned with hiding the static method call and it looking > too much like a regular call, > we could do something similar to how method references work (::) - we > would just need some > simple way to make it look distinctly different. > > Example using double periods to denote imported static method call: > > return someRemoteCall() // step 1 > .thenApply(x -> x + x) // step 2 > .thenCompose(x -> otherRemoteCall(x)) // step 3 > ..exceptionallyCompose(e -> retryRemoteCall(e)) // step 4 > .thenApply(x -> transformResponse(x)) // step 5 > > Also, as is pointed out in this email thread, I agree that the fact > that many other languages > have tried to address this problem with some language feature, could > be an indicator > that it is in fact a problem worth solving. > > I have concluded that my original idea will not end up being > implemented, but I hope > that there is something simple that can be done to reduce the problem. > > Best regards > Kristofer From davidalayachew at gmail.com Tue Apr 4 20:47:53 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 4 Apr 2023 16:47:53 -0400 Subject: Language feature to support Fluent Syntax for Static Method Calls Message-ID: Hello Kristofer, I am not a part of the Amber Dev Team, but I think I see the reason. Like you said, you can compose various functions (such as Optional::map and Optional::get) to achieve essentially what Optional::transform would give you. However, I think the general idea is that, by the time you have placed your object into a CompletableFuture, CompletionStage, or an Optional, your object is not just a String, but some strongly typed Java object. This aligns with some of the posts about Data-Oriented Programming, where one of the tenants behind it is to leave the untyped objects at the edge and only let the strongly-typed objects in. With that assumption, Optional::transform only serves to be a shortcut for Optional::map -> Optional::get. It's a useful shortcut, but nothing more than a shortcut. As a result, many here might see it as a (comparatively) low value change. However, the default type for untyped objects coming from the outside world is String. As a result, String::transform is accomplishing a very different goal. It's not just a transform, it's also a deserialization method (or at least, a very important component within a deserialization method). Working with strongly typed objects is easy and allows you to add methods wherever you see fit. Therefore, providing a CompletableFuture::transform doesn't hold much value when there's CompletableFuture::apply and CompletableFuture::get. But STRINGLY typed objects (that is not a spelling error) are historically and notoriously difficult to work with and deserialize. And again, the default type of data coming from the outside world is String. Therefore, the need is much higher for a String::transform method, as you cannot extend java.lang.String, and we want to simplify deserialization as much as possible so that we can get away from being Stringly typed as quickly as possible. On top of that, you can extend and implement CompletableFuture and CompletionStage respectively, so you can easily add this transform method yourself, as you mentioned. All that said, I think asking for it for Optional makes enough sense to me. Since that class is final, you can't easily add a method onto it (typeclasses, please come soon!). That reason alone is worth asking for it. But like I said, there's not much value in it when it serves as nothing more than a shortcut for a class that, most would argue, it doesn't need. Let me know your thoughts. Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Wed Apr 5 09:58:19 2023 From: redio.development at gmail.com (Red IO) Date: Wed, 5 Apr 2023 11:58:19 +0200 Subject: Expanding the "expression syntax" In-Reply-To: References: Message-ID: I did some more research and testing and concluded that loop expressions are more difficult to achieve than I thought and are not as simple as I thought. So I limit my proposal to try expressions since I really think they are worth it. A try expression would follow this syntax: TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in every path)] [FINALLY_BLOCK (with no yield but throw possible)]; An example would be: int fileInt = try (var reader = new BufferedReader(new FileReader("test.txt"))) { var line = reader.readLine(); if (line == null) yield -1; yield Integer.parseInt(line); } catch (IOException e) { throw new CustomException("Couldn't open file", e); } catch (NumberFormatException e) { yield -1; } finally { if (outsideCondition) throw new CustomCancelledExecption(); System.out.println("file read done"); }; This example is constructed to feature all possible parts of the syntax and is not necessarily code that makes sense to write. Great regards RedIODev On Sat, Mar 4, 2023, 10:55 Red IO wrote: > Currently we have the switch expression as the only expression returning a > result. > Example: > boolean b = switch (1) { > case 0 -> false; > case 1 -> { > yield true; > } > }; > > The idea would be to expand this syntax to different expressions for > example the try expression: > > int i = try { > yield Integer.parseInt("Abc"); > } catch (NumberFormatException e) { > yield -1; > }; > > Or loops: > > String searched = for(String s : args) { > if (s.startsWith("-")) > yield s; > }; > > The idea is to make all java control flow expressions able to yield a > value. > > Among many new patterns possible like the examples above it would for > example "clean up" the exception catching initialization: > > Foo foo = null; > > try { > foo = new Foo(); > } catch (SomeException e) { > //do something or nothing > } finally { > //maybe change the value again > } > > To > > var foo = try { > yield new Foo(); > } catch (SomeException e) { > //throw or yield > } finally { > //throw or do nothing > }; > > Another benefit especially in this example is the clearer state of the > result. In an yielding expression you have to either yield or throw. Also > subsequent manipulation in for example the finally block is not possible > making the source of the returned result clear to identify. > > Great regards > RedIODev > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Wed Apr 5 11:15:27 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Wed, 5 Apr 2023 11:15:27 +0000 Subject: Assignments to finals in switch guards In-Reply-To: References: Message-ID: Good catch Robbe! Guards should not assign to variables declared outside the guard, otherwise the DA/DU analysis becomes very challenging, and we will end up removing the possibility of optimising switches. Thanks, Gavin On 25 Mar 2023, at 21:45, Robbe Pincket wrote: Hi all It seems that the following code still compiles in Java 20. ```java class Test { public static void main(String[] args) { System.out.println("Got: " + Test(17)); } static boolean log(Object o) { System.out.println("Log:" + o); return false; } static String test(Object o) { final String s; switch (o) { case Integer i when (s = "a") != null && log(s) -> { return s; } case Integer i when (s = "b") != null && log(s) -> { return s; } case Integer i when (s = "c") != null && log(s) -> { return s; } case Object o1 when (s = "d") != null && log(s) || true -> { return s; } default -> { throw new IllegalStateException("Unreachable"); } } } } ``` The code above reassigns the final variable `s` multiple times and prints: ``` Log: a Log: b Log: c Log: d Got: d ``` Similarly the following code: ```java class Test { public static void main(String[] args) { System.out.println("Got: " + new Test(17).s); } boolean log(Object o) { System.out.println("Log: " + o + ", " + this.s); return false; } final String s; Test(Object o) { switch (o) { case Integer i when(s = "a") != null && log(s) -> { } case Integer i when(s = "b") != null && log(s) -> { } case Integer i when(s = "c") != null && log(s) -> { } case Object o1 when(s = "d") != null && log(s) || true -> { } default -> { throw new IllegalStateException("Unreachable"); } } } } ``` Also compiles and produces the following ``` Log: a, a Log: b, b Log: c, c Log: d, d Got: d ``` Showing the final field being reassigned multiple times. Right before sending this email, I just remembered that the first example shouldn't compile, as in each `return s;` the `s` variable shouldn't be considered to be definite assigned. Kind regards Robbe Pincket -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Wed Apr 5 12:55:15 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Wed, 5 Apr 2023 14:55:15 +0200 Subject: Expanding the "expression syntax" In-Reply-To: References: Message-ID: Hello! Just for the record, three years ago I posted a similar, though more universal proposal, "do expressions": https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html With best regards, Tagir Valeev. On Wed, Apr 5, 2023 at 11:58?AM Red IO wrote: > I did some more research and testing and concluded that loop expressions > are more difficult to achieve than I thought and are not as simple as I > thought. > So I limit my proposal to try expressions since I really think they are > worth it. > A try expression would follow this syntax: > TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path > leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in > every path)] > [FINALLY_BLOCK (with no yield but throw possible)]; > > An example would be: > int fileInt = try (var reader = new BufferedReader(new > FileReader("test.txt"))) { > var line = reader.readLine(); > if (line == null) > yield -1; > yield Integer.parseInt(line); > } catch (IOException e) { > throw new CustomException("Couldn't open file", e); > } catch (NumberFormatException e) { > yield -1; > } finally { > if (outsideCondition) > throw new CustomCancelledExecption(); > System.out.println("file read done"); > }; > > This example is constructed to feature all possible parts of the syntax > and is not necessarily code that makes sense to write. > > Great regards > RedIODev > > On Sat, Mar 4, 2023, 10:55 Red IO wrote: > >> Currently we have the switch expression as the only expression returning >> a result. >> Example: >> boolean b = switch (1) { >> case 0 -> false; >> case 1 -> { >> yield true; >> } >> }; >> >> The idea would be to expand this syntax to different expressions for >> example the try expression: >> >> int i = try { >> yield Integer.parseInt("Abc"); >> } catch (NumberFormatException e) { >> yield -1; >> }; >> >> Or loops: >> >> String searched = for(String s : args) { >> if (s.startsWith("-")) >> yield s; >> }; >> >> The idea is to make all java control flow expressions able to yield a >> value. >> >> Among many new patterns possible like the examples above it would for >> example "clean up" the exception catching initialization: >> >> Foo foo = null; >> >> try { >> foo = new Foo(); >> } catch (SomeException e) { >> //do something or nothing >> } finally { >> //maybe change the value again >> } >> >> To >> >> var foo = try { >> yield new Foo(); >> } catch (SomeException e) { >> //throw or yield >> } finally { >> //throw or do nothing >> }; >> >> Another benefit especially in this example is the clearer state of the >> result. In an yielding expression you have to either yield or throw. Also >> subsequent manipulation in for example the finally block is not possible >> making the source of the returned result clear to identify. >> >> Great regards >> RedIODev >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Wed Apr 5 14:27:16 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Wed, 5 Apr 2023 17:27:16 +0300 Subject: Expanding the "expression syntax" In-Reply-To: References: Message-ID: A different approach is to give `yield` the same semantics as `break`, and interpreting `break label` as `yield label null`, this would make all label blocks be expressions. The only thing we need to be careful with is finally block, but this situation already happens with `return` inside of a finally block, so I don't think we need to handle this situation differently On Wed, Apr 5, 2023, 15:55 Tagir Valeev wrote: > Hello! > > Just for the record, three years ago I posted a similar, though more > universal proposal, "do expressions": > > https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html > > With best regards, > Tagir Valeev. > > On Wed, Apr 5, 2023 at 11:58?AM Red IO > wrote: > >> I did some more research and testing and concluded that loop expressions >> are more difficult to achieve than I thought and are not as simple as I >> thought. >> So I limit my proposal to try expressions since I really think they are >> worth it. >> A try expression would follow this syntax: >> TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path >> leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in >> every path)] >> [FINALLY_BLOCK (with no yield but throw possible)]; >> >> An example would be: >> int fileInt = try (var reader = new BufferedReader(new >> FileReader("test.txt"))) { >> var line = reader.readLine(); >> if (line == null) >> yield -1; >> yield Integer.parseInt(line); >> } catch (IOException e) { >> throw new CustomException("Couldn't open file", e); >> } catch (NumberFormatException e) { >> yield -1; >> } finally { >> if (outsideCondition) >> throw new CustomCancelledExecption(); >> System.out.println("file read done"); >> }; >> >> This example is constructed to feature all possible parts of the syntax >> and is not necessarily code that makes sense to write. >> >> Great regards >> RedIODev >> >> On Sat, Mar 4, 2023, 10:55 Red IO wrote: >> >>> Currently we have the switch expression as the only expression returning >>> a result. >>> Example: >>> boolean b = switch (1) { >>> case 0 -> false; >>> case 1 -> { >>> yield true; >>> } >>> }; >>> >>> The idea would be to expand this syntax to different expressions for >>> example the try expression: >>> >>> int i = try { >>> yield Integer.parseInt("Abc"); >>> } catch (NumberFormatException e) { >>> yield -1; >>> }; >>> >>> Or loops: >>> >>> String searched = for(String s : args) { >>> if (s.startsWith("-")) >>> yield s; >>> }; >>> >>> The idea is to make all java control flow expressions able to yield a >>> value. >>> >>> Among many new patterns possible like the examples above it would for >>> example "clean up" the exception catching initialization: >>> >>> Foo foo = null; >>> >>> try { >>> foo = new Foo(); >>> } catch (SomeException e) { >>> //do something or nothing >>> } finally { >>> //maybe change the value again >>> } >>> >>> To >>> >>> var foo = try { >>> yield new Foo(); >>> } catch (SomeException e) { >>> //throw or yield >>> } finally { >>> //throw or do nothing >>> }; >>> >>> Another benefit especially in this example is the clearer state of the >>> result. In an yielding expression you have to either yield or throw. Also >>> subsequent manipulation in for example the finally block is not possible >>> making the source of the returned result clear to identify. >>> >>> Great regards >>> RedIODev >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Wed Apr 5 17:17:54 2023 From: redio.development at gmail.com (Red IO) Date: Wed, 5 Apr 2023 19:17:54 +0200 Subject: Expanding the "expression syntax" In-Reply-To: References: Message-ID: I actually tried this exactly with annotation processor "hacks" I then realized that a loop might execute 0 times or never conditionally hit a yield statements. This results in the expression sometimes not yielding a result which wouldn't allow an assignment to a final variable and confusing errors like "variable might not be initialized" the only solution would be to add an else block to a loop that os executed when no yield was hit in the loop body (which would be ugly and confusing in my opinion) That's why I stepped down from generalizing yield to all control flow elements to specially allow yield in try. Great regards RedIODev On Wed, Apr 5, 2023, 16:27 Holo The Sage Wolf wrote: > A different approach is to give `yield` the same semantics as `break`, and > interpreting `break label` as `yield label null`, this would make all label > blocks be expressions. > > The only thing we need to be careful with is finally block, but this > situation already happens with `return` inside of a finally block, so I > don't think we need to handle this situation differently > > On Wed, Apr 5, 2023, 15:55 Tagir Valeev wrote: > >> Hello! >> >> Just for the record, three years ago I posted a similar, though more >> universal proposal, "do expressions": >> >> https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html >> >> With best regards, >> Tagir Valeev. >> >> On Wed, Apr 5, 2023 at 11:58?AM Red IO >> wrote: >> >>> I did some more research and testing and concluded that loop expressions >>> are more difficult to achieve than I thought and are not as simple as I >>> thought. >>> So I limit my proposal to try expressions since I really think they are >>> worth it. >>> A try expression would follow this syntax: >>> TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path >>> leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in >>> every path)] >>> [FINALLY_BLOCK (with no yield but throw possible)]; >>> >>> An example would be: >>> int fileInt = try (var reader = new BufferedReader(new >>> FileReader("test.txt"))) { >>> var line = reader.readLine(); >>> if (line == null) >>> yield -1; >>> yield Integer.parseInt(line); >>> } catch (IOException e) { >>> throw new CustomException("Couldn't open file", e); >>> } catch (NumberFormatException e) { >>> yield -1; >>> } finally { >>> if (outsideCondition) >>> throw new CustomCancelledExecption(); >>> System.out.println("file read done"); >>> }; >>> >>> This example is constructed to feature all possible parts of the syntax >>> and is not necessarily code that makes sense to write. >>> >>> Great regards >>> RedIODev >>> >>> On Sat, Mar 4, 2023, 10:55 Red IO wrote: >>> >>>> Currently we have the switch expression as the only expression >>>> returning a result. >>>> Example: >>>> boolean b = switch (1) { >>>> case 0 -> false; >>>> case 1 -> { >>>> yield true; >>>> } >>>> }; >>>> >>>> The idea would be to expand this syntax to different expressions for >>>> example the try expression: >>>> >>>> int i = try { >>>> yield Integer.parseInt("Abc"); >>>> } catch (NumberFormatException e) { >>>> yield -1; >>>> }; >>>> >>>> Or loops: >>>> >>>> String searched = for(String s : args) { >>>> if (s.startsWith("-")) >>>> yield s; >>>> }; >>>> >>>> The idea is to make all java control flow expressions able to yield a >>>> value. >>>> >>>> Among many new patterns possible like the examples above it would for >>>> example "clean up" the exception catching initialization: >>>> >>>> Foo foo = null; >>>> >>>> try { >>>> foo = new Foo(); >>>> } catch (SomeException e) { >>>> //do something or nothing >>>> } finally { >>>> //maybe change the value again >>>> } >>>> >>>> To >>>> >>>> var foo = try { >>>> yield new Foo(); >>>> } catch (SomeException e) { >>>> //throw or yield >>>> } finally { >>>> //throw or do nothing >>>> }; >>>> >>>> Another benefit especially in this example is the clearer state of the >>>> result. In an yielding expression you have to either yield or throw. Also >>>> subsequent manipulation in for example the finally block is not possible >>>> making the source of the returned result clear to identify. >>>> >>>> Great regards >>>> RedIODev >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From adowrath+ml at protonmail.ch Wed Apr 5 17:35:30 2023 From: adowrath+ml at protonmail.ch (Adowrath) Date: Wed, 05 Apr 2023 17:35:30 +0000 Subject: Expanding the "expression syntax" In-Reply-To: References: Message-ID: Actually, regarding the `else` block/branch for a loop, there's almost a precedence for this in Python: for and while loops can have an `else` block that executes after the loop has finished, iff no break statement (or, of course, return) was hit inside of the loop, only that this doesn't elevate the loop to an expression. Using the same logic for yielding from an expression loop could work equally - the downside is that even in Python it's considered quite uncommon/unusual syntax. Regards, Adowrath (Sorry for the repeat mail to you RedIODev; forgot to put the +ml into my sender address so it didn't get through to the mailing list) Am 2023-04-05 um 19:17 schrieb Red IO: > I actually tried this exactly with annotation processor "hacks" I then realized that a loop might execute 0 times or never conditionally hit a yield statements. This results in the expression sometimes not yielding a result which wouldn't allow an assignment to a final variable and confusing errors like "variable might not be initialized" the only solution would be to add an else block to a loop that os executed when no yield was hit in the loop body (which would be ugly and confusing in my opinion) > That's why I stepped down from generalizing yield to all control flow elements to specially allow yield in try. > > Great regards > RedIODev > > On Wed, Apr 5, 2023, 16:27 Holo The Sage Wolf wrote: > >> A different approach is to give `yield` the same semantics as `break`, and interpreting `break label` as `yield label null`, this would make all label blocks be expressions. >> >> The only thing we need to be careful with is finally block, but this situation already happens with `return` inside of a finally block, so I don't think we need to handle this situation differently >> >> On Wed, Apr 5, 2023, 15:55 Tagir Valeev wrote: >> >>> Hello! >>> >>> Just for the record, three years ago I posted a similar, though more universal proposal, "do expressions": >>> https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html >>> >>> With best regards, >>> Tagir Valeev. >>> >>> On Wed, Apr 5, 2023 at 11:58?AM Red IO wrote: >>> >>>> I did some more research and testing and concluded that loop expressions are more difficult to achieve than I thought and are not as simple as I thought. >>>> So I limit my proposal to try expressions since I really think they are worth it. >>>> A try expression would follow this syntax: >>>> TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in every path)] >>>> [FINALLY_BLOCK (with no yield but throw possible)]; >>>> >>>> An example would be: >>>> int fileInt = try (var reader = new BufferedReader(new FileReader("test.txt"))) { >>>> var line = reader.readLine(); >>>> if (line == null) >>>> yield -1; >>>> yield Integer.parseInt(line); >>>> } catch (IOException e) { >>>> throw new CustomException("Couldn't open file", e); >>>> } catch (NumberFormatException e) { >>>> yield -1; >>>> } finally { >>>> if (outsideCondition) >>>> throw new CustomCancelledExecption(); >>>> System.out.println("file read done"); >>>> }; >>>> >>>> This example is constructed to feature all possible parts of the syntax and is not necessarily code that makes sense to write. >>>> >>>> Great regards >>>> RedIODev >>>> >>>> On Sat, Mar 4, 2023, 10:55 Red IO wrote: >>>> >>>>> Currently we have the switch expression as the only expression returning a result. >>>>> Example: >>>>> boolean b = switch (1) { >>>>> case 0 -> false; >>>>> case 1 -> { >>>>> yield true; >>>>> } >>>>> }; >>>>> >>>>> The idea would be to expand this syntax to different expressions for example the try expression: >>>>> >>>>> int i = try { >>>>> yield Integer.parseInt("Abc"); >>>>> } catch (NumberFormatException e) { >>>>> yield -1; >>>>> }; >>>>> >>>>> Or loops: >>>>> >>>>> String searched = for(String s : args) { >>>>> if (s.startsWith("-")) >>>>> yield s; >>>>> }; >>>>> >>>>> The idea is to make all java control flow expressions able to yield a value. >>>>> >>>>> Among many new patterns possible like the examples above it would for example "clean up" the exception catching initialization: >>>>> >>>>> Foo foo = null; >>>>> >>>>> try { >>>>> foo = new Foo(); >>>>> } catch (SomeException e) { >>>>> //do something or nothing >>>>> } finally { >>>>> //maybe change the value again >>>>> } >>>>> >>>>> To >>>>> >>>>> var foo = try { >>>>> yield new Foo(); >>>>> } catch (SomeException e) { >>>>> //throw or yield >>>>> } finally { >>>>> //throw or do nothing >>>>> }; >>>>> >>>>> Another benefit especially in this example is the clearer state of the result. In an yielding expression you have to either yield or throw. Also subsequent manipulation in for example the finally block is not possible making the source of the returned result clear to identify. >>>>> >>>>> Great regards >>>>> RedIODev -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Wed Apr 5 17:51:04 2023 From: redio.development at gmail.com (Red IO) Date: Wed, 5 Apr 2023 19:51:04 +0200 Subject: Expanding the "expression syntax" In-Reply-To: <01374248-bf7c-87c3-5556-73c7fa9b7e4c@protonmail.ch> References: <01374248-bf7c-87c3-5556-73c7fa9b7e4c@protonmail.ch> Message-ID: Yes I know this python syntax. To make it clear saying that I'm not a fan of python would be an understatement. But even the python creator labeled the loop else as his biggest mistake. And adding "the biggest mistake" of my least favorite language to java is definitely not in my interest. When you look in another direction like rust which already features the "everything is an expression" mindset you can see that for and while loops where purposefully left out because of this exact dilemma. Rust features a special endless loop expression which can be an expression when static analysis can confirm that "yield" will be hit eventually or loop forever. But I don't see java adding this loop type and treating a "while(true)" which is the practical equivalent specially would be weird. Great regards RedIODev On Wed, Apr 5, 2023, 19:27 Adowrath wrote: > Actually, regarding the `else` block/branch for a loop, there's almost a > precedence for this in Python: for and while loops can have an `else` block > that executes after the loop has finished, iff no break statement (or, of > course, return) was hit inside of the loop, only that this doesn't elevate > the loop to an expression. Using the same logic for yielding from an > expression loop could work equally - the downside is that even in Python > it's considered quite uncommon/unusual syntax. > > Regards, > Adowrath > Am 2023-04-05 um 19:17 schrieb Red IO: > > I actually tried this exactly with annotation processor "hacks" I then > realized that a loop might execute 0 times or never conditionally hit a > yield statements. This results in the expression sometimes not yielding a > result which wouldn't allow an assignment to a final variable and confusing > errors like "variable might not be initialized" the only solution would be > to add an else block to a loop that os executed when no yield was hit in > the loop body (which would be ugly and confusing in my opinion) > That's why I stepped down from generalizing yield to all control flow > elements to specially allow yield in try. > > Great regards > RedIODev > > On Wed, Apr 5, 2023, 16:27 Holo The Sage Wolf wrote: > >> A different approach is to give `yield` the same semantics as `break`, >> and interpreting `break label` as `yield label null`, this would make all >> label blocks be expressions. >> >> The only thing we need to be careful with is finally block, but this >> situation already happens with `return` inside of a finally block, so I >> don't think we need to handle this situation differently >> >> On Wed, Apr 5, 2023, 15:55 Tagir Valeev wrote: >> >>> Hello! >>> >>> Just for the record, three years ago I posted a similar, though more >>> universal proposal, "do expressions": >>> >>> https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html >>> >>> With best regards, >>> Tagir Valeev. >>> >>> On Wed, Apr 5, 2023 at 11:58?AM Red IO >>> wrote: >>> >>>> I did some more research and testing and concluded that loop >>>> expressions are more difficult to achieve than I thought and are not as >>>> simple as I thought. >>>> So I limit my proposal to try expressions since I really think they are >>>> worth it. >>>> A try expression would follow this syntax: >>>> TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path >>>> leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in >>>> every path)] >>>> [FINALLY_BLOCK (with no yield but throw possible)]; >>>> >>>> An example would be: >>>> int fileInt = try (var reader = new BufferedReader(new >>>> FileReader("test.txt"))) { >>>> var line = reader.readLine(); >>>> if (line == null) >>>> yield -1; >>>> yield Integer.parseInt(line); >>>> } catch (IOException e) { >>>> throw new CustomException("Couldn't open file", e); >>>> } catch (NumberFormatException e) { >>>> yield -1; >>>> } finally { >>>> if (outsideCondition) >>>> throw new CustomCancelledExecption(); >>>> System.out.println("file read done"); >>>> }; >>>> >>>> This example is constructed to feature all possible parts of the syntax >>>> and is not necessarily code that makes sense to write. >>>> >>>> Great regards >>>> RedIODev >>>> >>>> On Sat, Mar 4, 2023, 10:55 Red IO wrote: >>>> >>>>> Currently we have the switch expression as the only expression >>>>> returning a result. >>>>> Example: >>>>> boolean b = switch (1) { >>>>> case 0 -> false; >>>>> case 1 -> { >>>>> yield true; >>>>> } >>>>> }; >>>>> >>>>> The idea would be to expand this syntax to different expressions for >>>>> example the try expression: >>>>> >>>>> int i = try { >>>>> yield Integer.parseInt("Abc"); >>>>> } catch (NumberFormatException e) { >>>>> yield -1; >>>>> }; >>>>> >>>>> Or loops: >>>>> >>>>> String searched = for(String s : args) { >>>>> if (s.startsWith("-")) >>>>> yield s; >>>>> }; >>>>> >>>>> The idea is to make all java control flow expressions able to yield a >>>>> value. >>>>> >>>>> Among many new patterns possible like the examples above it would for >>>>> example "clean up" the exception catching initialization: >>>>> >>>>> Foo foo = null; >>>>> >>>>> try { >>>>> foo = new Foo(); >>>>> } catch (SomeException e) { >>>>> //do something or nothing >>>>> } finally { >>>>> //maybe change the value again >>>>> } >>>>> >>>>> To >>>>> >>>>> var foo = try { >>>>> yield new Foo(); >>>>> } catch (SomeException e) { >>>>> //throw or yield >>>>> } finally { >>>>> //throw or do nothing >>>>> }; >>>>> >>>>> Another benefit especially in this example is the clearer state of the >>>>> result. In an yielding expression you have to either yield or throw. Also >>>>> subsequent manipulation in for example the finally block is not possible >>>>> making the source of the returned result clear to identify. >>>>> >>>>> Great regards >>>>> RedIODev >>>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From tom_L64 at hotmail.com Wed Apr 5 18:34:21 2023 From: tom_L64 at hotmail.com (tom L) Date: Wed, 5 Apr 2023 18:34:21 +0000 Subject: =?utf-8?B?UkXCoDogRXhwYW5kaW5nIHRoZSAiZXhwcmVzc2lvbiBzeW50YXgi?= In-Reply-To: References: Message-ID: Hello, This sounds like a cool idea, but why use a keyword (do) at the first place ? I read the discussion, which mostly was about the do keyword, but I feel like there shouldn?t be a keyword at the first place. int foo = { yield 5 ; } Blocks already exist, and while they do not return anything currently, you could make so if there is a yield, it would mean it can return a value basically. It?s similar in my opinion to how you can assign the result of a void method, but you can from any other return type, except that here it?s inferred. It can also be seen as a generalization of a switch statement. (Sorry if my vocabulary is off, but I hope you see my point) Great regards From : Tagir Valeev To : Red IO Cc : amber-dev Object :Re: Expanding the "expression syntax" Hello! Just for the record, three years ago I posted a similar, though more universal proposal, "do expressions": https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html With best regards, Tagir Valeev. On Wed, Apr 5, 2023 at 11:58?AM Red IO > wrote: I did some more research and testing and concluded that loop expressions are more difficult to achieve than I thought and are not as simple as I thought. So I limit my proposal to try expressions since I really think they are worth it. A try expression would follow this syntax: TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in every path)] [FINALLY_BLOCK (with no yield but throw possible)]; An example would be: int fileInt = try (var reader = new BufferedReader(new FileReader("test.txt"))) { var line = reader.readLine(); if (line == null) yield -1; yield Integer.parseInt(line); } catch (IOException e) { throw new CustomException("Couldn't open file", e); } catch (NumberFormatException e) { yield -1; } finally { if (outsideCondition) throw new CustomCancelledExecption(); System.out.println("file read done"); }; This example is constructed to feature all possible parts of the syntax and is not necessarily code that makes sense to write. Great regards RedIODev On Sat, Mar 4, 2023, 10:55 Red IO > wrote: Currently we have the switch expression as the only expression returning a result. Example: boolean b = switch (1) { case 0 -> false; case 1 -> { yield true; } }; The idea would be to expand this syntax to different expressions for example the try expression: int i = try { yield Integer.parseInt("Abc"); } catch (NumberFormatException e) { yield -1; }; Or loops: String searched = for(String s : args) { if (s.startsWith("-")) yield s; }; The idea is to make all java control flow expressions able to yield a value. Among many new patterns possible like the examples above it would for example "clean up" the exception catching initialization: Foo foo = null; try { foo = new Foo(); } catch (SomeException e) { //do something or nothing } finally { //maybe change the value again } To var foo = try { yield new Foo(); } catch (SomeException e) { //throw or yield } finally { //throw or do nothing }; Another benefit especially in this example is the clearer state of the result. In an yielding expression you have to either yield or throw. Also subsequent manipulation in for example the finally block is not possible making the source of the returned result clear to identify. Great regards RedIODev -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Apr 5 18:56:36 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 5 Apr 2023 18:56:36 +0000 Subject: Expanding the "expression syntax" In-Reply-To: References: <01374248-bf7c-87c3-5556-73c7fa9b7e4c@protonmail.ch> Message-ID: <1201B102-F019-4395-9F1C-6DAFEBF3F6AD@oracle.com> Just a gentle reminder ? this is straying pretty far outside the charter of amber-dev. Amber-dev is primarily for collaboration on the _implementation_ of Project Amber. This includes things like bug reports and experience reports, ?how does it work? queries, etc. We do interpret the charter pretty broadly, and so when discussion briefly steps outside the bounds, that?s usually not a problem. But, amber-dev is definitely *not* the ?I have a new idea for Java? discussion list. With 10M developers, and each of them having two or three favorite feature ideas, such discussion could easily overwhelm the actual work that is going on. So probably best to mostly limit to features that are actually under development within Project Amber. Thanks, -Brian On Apr 5, 2023, at 10:51 AM, Red IO > wrote: Yes I know this python syntax. To make it clear saying that I'm not a fan of python would be an understatement. But even the python creator labeled the loop else as his biggest mistake. And adding "the biggest mistake" of my least favorite language to java is definitely not in my interest. When you look in another direction like rust which already features the "everything is an expression" mindset you can see that for and while loops where purposefully left out because of this exact dilemma. Rust features a special endless loop expression which can be an expression when static analysis can confirm that "yield" will be hit eventually or loop forever. But I don't see java adding this loop type and treating a "while(true)" which is the practical equivalent specially would be weird. Great regards RedIODev On Wed, Apr 5, 2023, 19:27 Adowrath > wrote: Actually, regarding the `else` block/branch for a loop, there's almost a precedence for this in Python: for and while loops can have an `else` block that executes after the loop has finished, iff no break statement (or, of course, return) was hit inside of the loop, only that this doesn't elevate the loop to an expression. Using the same logic for yielding from an expression loop could work equally - the downside is that even in Python it's considered quite uncommon/unusual syntax. Regards, Adowrath Am 2023-04-05 um 19:17 schrieb Red IO: I actually tried this exactly with annotation processor "hacks" I then realized that a loop might execute 0 times or never conditionally hit a yield statements. This results in the expression sometimes not yielding a result which wouldn't allow an assignment to a final variable and confusing errors like "variable might not be initialized" the only solution would be to add an else block to a loop that os executed when no yield was hit in the loop body (which would be ugly and confusing in my opinion) That's why I stepped down from generalizing yield to all control flow elements to specially allow yield in try. Great regards RedIODev On Wed, Apr 5, 2023, 16:27 Holo The Sage Wolf > wrote: A different approach is to give `yield` the same semantics as `break`, and interpreting `break label` as `yield label null`, this would make all label blocks be expressions. The only thing we need to be careful with is finally block, but this situation already happens with `return` inside of a finally block, so I don't think we need to handle this situation differently On Wed, Apr 5, 2023, 15:55 Tagir Valeev > wrote: Hello! Just for the record, three years ago I posted a similar, though more universal proposal, "do expressions": https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html With best regards, Tagir Valeev. On Wed, Apr 5, 2023 at 11:58?AM Red IO > wrote: I did some more research and testing and concluded that loop expressions are more difficult to achieve than I thought and are not as simple as I thought. So I limit my proposal to try expressions since I really think they are worth it. A try expression would follow this syntax: TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in every path)] [FINALLY_BLOCK (with no yield but throw possible)]; An example would be: int fileInt = try (var reader = new BufferedReader(new FileReader("test.txt"))) { var line = reader.readLine(); if (line == null) yield -1; yield Integer.parseInt(line); } catch (IOException e) { throw new CustomException("Couldn't open file", e); } catch (NumberFormatException e) { yield -1; } finally { if (outsideCondition) throw new CustomCancelledExecption(); System.out.println("file read done"); }; This example is constructed to feature all possible parts of the syntax and is not necessarily code that makes sense to write. Great regards RedIODev On Sat, Mar 4, 2023, 10:55 Red IO > wrote: Currently we have the switch expression as the only expression returning a result. Example: boolean b = switch (1) { case 0 -> false; case 1 -> { yield true; } }; The idea would be to expand this syntax to different expressions for example the try expression: int i = try { yield Integer.parseInt("Abc"); } catch (NumberFormatException e) { yield -1; }; Or loops: String searched = for(String s : args) { if (s.startsWith("-")) yield s; }; The idea is to make all java control flow expressions able to yield a value. Among many new patterns possible like the examples above it would for example "clean up" the exception catching initialization: Foo foo = null; try { foo = new Foo(); } catch (SomeException e) { //do something or nothing } finally { //maybe change the value again } To var foo = try { yield new Foo(); } catch (SomeException e) { //throw or yield } finally { //throw or do nothing }; Another benefit especially in this example is the clearer state of the result. In an yielding expression you have to either yield or throw. Also subsequent manipulation in for example the finally block is not possible making the source of the returned result clear to identify. Great regards RedIODev -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Wed Apr 5 20:56:04 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 5 Apr 2023 22:56:04 +0200 (CEST) Subject: Bimodal compilation Message-ID: <795367426.30059295.1680728164210.JavaMail.zimbra@univ-eiffel.fr> Hi all, for Devoxx France, me and Jos? Paumard are giving a talk about Valhalla and Amber with several benchmarks mixing the two. One problem we have is that the way pattern matching is compiled by javac and later JIT compiled leads to bimodal performance. Depending on the day (exactly, depending on the JIT threads scheduling), either the method containing the switch is compiled as a whole method (good day) or only the method handle (corresponding to the invokedynamic) is compiled and when the whole method is compiled, the assembly code corresponding to the method handle is considered as too big thus not inlined (bad day). The example is using arrays of non null instance of value classes with a default instance (the kind of value classes that are flattened in memory) and a sealed interface, if the method is not fully inlined (good day) performance have very good and if not performance are terrible (bad day). A cascade of instanceof while slightly less fast does not exhibit that issue. In our example, this bimodal performance issue is hidden when using identity classes, because of the cache misses become the bottleneck. I'm a little worry here because I do not see how to fix this bug without changing the way switch on types are compiled by javac, so this problem has to be tackled before the JDK 21 is released otherwise, people will have to recompile their application to fix that bug. regards, R?mi --- public @ZeroDefault @Value record Population(int amount) { public static Population zero() { return new Population(0); } public Population add(Population other) { return new Population(this.amount + other.amount); } } public sealed interface Populated permits City, Department, Region {} public @ZeroDefault @Value record City(String name, @NonNull Population population) implements Populated {} public @ZeroDefault @Value record Department(String name, City[] cities) implements Populated { public Department { cities = Arrays.stream(cities).toArray(size -> RT.newNonNullArray(City.class, size)); } } public @ZeroDefault @Value record Region(String name, Department[] departments) implements Populated { public Region { departments = Arrays.stream(departments).toArray(size -> RT.newNonNullArray(Department.class, size)); } } public static Population sumPopulationOf(Populated populated) { return switch (populated) { case City(var name, var population) -> population; case Department(var name, var cities) -> { var sum = Population.zero(); for(var city: cities) { sum = sum.add(sumPopulationOf(city)); } yield sum; } case Region(var name, var departments) -> { var sum = Population.zero(); for(var department: departments) { sum = sum.add(sumPopulationOf(department)); } yield sum; } }; } public static Population sumPopulationOf(Populated[] populateds) { var sum = Population.zero(); for(var populated: populateds) { sum = sum.add(sumPopulationOf(populated)); } return sum; } --- public class BenchDOP { private Region[] regions; @Setup public void init() { var data = Data.readCities(); regions = data.regions().toArray(size -> RT.newNonNullArray(Region.class, size)); } @Benchmark public Population sumPopulations() { return Data.sumPopulationOf(regions); } } --- # Benchmark: org.paumard.amber.model.cityvaluenonnullablearraydrecords.BenchDOP.sumPopulations # Run progress: 0.00% complete, ETA 00:02:06 # Fork: 1 of 3 # Warmup Iteration 1: 47.845 us/op # Warmup Iteration 2: 38.199 us/op # Warmup Iteration 3: 38.130 us/op # Warmup Iteration 4: 37.909 us/op # Warmup Iteration 5: 38.345 us/op Iteration 1: 38.581 us/op Iteration 2: 37.946 us/op Iteration 3: 37.837 us/op Iteration 4: 38.013 us/op Iteration 5: 37.885 us/op Iteration 6: 37.853 us/op Iteration 7: 37.931 us/op Iteration 8: 37.874 us/op Iteration 9: 37.828 us/op Iteration 10: 37.925 us/op <--- good day # Run progress: 8.33% complete, ETA 00:02:00 # Fork: 2 of 3 # Warmup Iteration 1: 2871.011 us/op # Warmup Iteration 2: 2761.856 us/op # Warmup Iteration 3: 2759.977 us/op # Warmup Iteration 4: 2761.045 us/op # Warmup Iteration 5: 2756.167 us/op Iteration 1: 2755.180 us/op Iteration 2: 2781.178 us/op Iteration 3: 2759.068 us/op Iteration 4: 2755.737 us/op Iteration 5: 2755.112 us/op Iteration 6: 2754.553 us/op Iteration 7: 2761.759 us/op Iteration 8: 2750.829 us/op Iteration 9: 2751.265 us/op Iteration 10: 2749.668 us/op <--- bad day # Run progress: 16.67% complete, ETA 00:01:48 # Fork: 3 of 3 # Warmup Iteration 1: 42.359 us/op # Warmup Iteration 2: 38.322 us/op # Warmup Iteration 3: 38.311 us/op # Warmup Iteration 4: 37.990 us/op # Warmup Iteration 5: 37.988 us/op Iteration 1: 38.139 us/op Iteration 2: 38.052 us/op Iteration 3: 37.959 us/op Iteration 4: 38.037 us/op Iteration 5: 37.997 us/op Iteration 6: 37.957 us/op Iteration 7: 37.977 us/op Iteration 8: 37.905 us/op Iteration 9: 37.913 us/op Iteration 10: 37.976 us/op <--- good day From forax at univ-mlv.fr Wed Apr 5 21:05:54 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Wed, 5 Apr 2023 23:05:54 +0200 (CEST) Subject: Bimodal compilation In-Reply-To: <795367426.30059295.1680728164210.JavaMail.zimbra@univ-eiffel.fr> References: <795367426.30059295.1680728164210.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <1621877955.30061527.1680728754364.JavaMail.zimbra@univ-eiffel.fr> oops, please do not take into account this message ! I've not read the LogCompilation file correctly, there is a bimodal issue but due to "sumPopulationOf(Populated[] populateds)" being inlinined or not, not due to how the switch is compiled. regards, R?mi ----- Original Message ----- > From: "Remi Forax" > To: "amber-dev" > Cc: "jan lahoda" > Sent: Wednesday, April 5, 2023 10:56:04 PM > Subject: Bimodal compilation > Hi all, > for Devoxx France, me and Jos? Paumard are giving a talk about Valhalla and > Amber with several benchmarks mixing the two. > > One problem we have is that the way pattern matching is compiled by javac and > later JIT compiled leads to bimodal performance. > Depending on the day (exactly, depending on the JIT threads scheduling), either > the method containing the switch is compiled as a whole method (good day) or > only the method handle (corresponding to the invokedynamic) is compiled and > when the whole method is compiled, the assembly code corresponding to the > method handle is considered as too big thus not inlined (bad day). > > The example is using arrays of non null instance of value classes with a default > instance (the kind of value classes that are flattened in memory) and a sealed > interface, if the method is not fully inlined (good day) performance have very > good and if not performance are terrible (bad day). > > A cascade of instanceof while slightly less fast does not exhibit that issue. > > In our example, this bimodal performance issue is hidden when using identity > classes, because of the cache misses become the bottleneck. > > I'm a little worry here because I do not see how to fix this bug without > changing the way switch on types are compiled by javac, so this problem has to > be tackled before the JDK 21 is released otherwise, people will have to > recompile their application to fix that bug. > > regards, > R?mi > > --- > > public @ZeroDefault @Value record Population(int amount) { > public static Population zero() { > return new Population(0); > } > public Population add(Population other) { > return new Population(this.amount + other.amount); > } > } > public sealed interface Populated permits City, Department, Region {} > public @ZeroDefault @Value record City(String name, @NonNull Population > population) implements Populated {} > public @ZeroDefault @Value record Department(String name, City[] cities) > implements Populated { > public Department { > cities = Arrays.stream(cities).toArray(size -> RT.newNonNullArray(City.class, > size)); > } > } > public @ZeroDefault @Value record Region(String name, Department[] departments) > implements Populated { > public Region { > departments = Arrays.stream(departments).toArray(size -> > RT.newNonNullArray(Department.class, size)); > } > } > > public static Population sumPopulationOf(Populated populated) { > return switch (populated) { > case City(var name, var population) -> population; > case Department(var name, var cities) -> { > var sum = Population.zero(); > for(var city: cities) { > sum = sum.add(sumPopulationOf(city)); > } > yield sum; > } > case Region(var name, var departments) -> { > var sum = Population.zero(); > for(var department: departments) { > sum = sum.add(sumPopulationOf(department)); > } > yield sum; > } > }; > } > > public static Population sumPopulationOf(Populated[] populateds) { > var sum = Population.zero(); > for(var populated: populateds) { > sum = sum.add(sumPopulationOf(populated)); > } > return sum; > } > > --- > > public class BenchDOP { > private Region[] regions; > > @Setup > public void init() { > var data = Data.readCities(); > regions = data.regions().toArray(size -> RT.newNonNullArray(Region.class, > size)); > } > > @Benchmark > public Population sumPopulations() { > return Data.sumPopulationOf(regions); > } > } > > > --- > > > > # Benchmark: > org.paumard.amber.model.cityvaluenonnullablearraydrecords.BenchDOP.sumPopulations > > # Run progress: 0.00% complete, ETA 00:02:06 > # Fork: 1 of 3 > # Warmup Iteration 1: 47.845 us/op > # Warmup Iteration 2: 38.199 us/op > # Warmup Iteration 3: 38.130 us/op > # Warmup Iteration 4: 37.909 us/op > # Warmup Iteration 5: 38.345 us/op > Iteration 1: 38.581 us/op > Iteration 2: 37.946 us/op > Iteration 3: 37.837 us/op > Iteration 4: 38.013 us/op > Iteration 5: 37.885 us/op > Iteration 6: 37.853 us/op > Iteration 7: 37.931 us/op > Iteration 8: 37.874 us/op > Iteration 9: 37.828 us/op > Iteration 10: 37.925 us/op <--- good day > > # Run progress: 8.33% complete, ETA 00:02:00 > # Fork: 2 of 3 > # Warmup Iteration 1: 2871.011 us/op > # Warmup Iteration 2: 2761.856 us/op > # Warmup Iteration 3: 2759.977 us/op > # Warmup Iteration 4: 2761.045 us/op > # Warmup Iteration 5: 2756.167 us/op > Iteration 1: 2755.180 us/op > Iteration 2: 2781.178 us/op > Iteration 3: 2759.068 us/op > Iteration 4: 2755.737 us/op > Iteration 5: 2755.112 us/op > Iteration 6: 2754.553 us/op > Iteration 7: 2761.759 us/op > Iteration 8: 2750.829 us/op > Iteration 9: 2751.265 us/op > Iteration 10: 2749.668 us/op <--- bad day > > # Run progress: 16.67% complete, ETA 00:01:48 > # Fork: 3 of 3 > # Warmup Iteration 1: 42.359 us/op > # Warmup Iteration 2: 38.322 us/op > # Warmup Iteration 3: 38.311 us/op > # Warmup Iteration 4: 37.990 us/op > # Warmup Iteration 5: 37.988 us/op > Iteration 1: 38.139 us/op > Iteration 2: 38.052 us/op > Iteration 3: 37.959 us/op > Iteration 4: 38.037 us/op > Iteration 5: 37.997 us/op > Iteration 6: 37.957 us/op > Iteration 7: 37.977 us/op > Iteration 8: 37.905 us/op > Iteration 9: 37.913 us/op > Iteration 10: 37.976 us/op <--- good day From claes.redestad at oracle.com Wed Apr 5 21:20:40 2023 From: claes.redestad at oracle.com (Claes Redestad) Date: Wed, 5 Apr 2023 21:20:40 +0000 Subject: Bimodal compilation In-Reply-To: <1621877955.30061527.1680728754364.JavaMail.zimbra@univ-eiffel.fr> References: <795367426.30059295.1680728164210.JavaMail.zimbra@univ-eiffel.fr> <1621877955.30061527.1680728754364.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <11A15FDE-D99B-4779-A06E-F2E8231D5649@oracle.com> Hi, I?d assume this is an issue with the hotspot JIT rather than with how javac happens to compile this particular program. Can you please provide the full program (or is @ZeroDefault some new OpenJDK construct everyone needs to be aware of?) /Claes > 5 apr. 2023 kl. 23:05 skrev Remi Forax : > > oops, please do not take into account this message ! > > I've not read the LogCompilation file correctly, there is a bimodal issue but due to "sumPopulationOf(Populated[] populateds)" being inlinined or not, not due to how the switch is compiled. > > regards, > R?mi > > ----- Original Message ----- >> From: "Remi Forax" >> To: "amber-dev" >> Cc: "jan lahoda" >> Sent: Wednesday, April 5, 2023 10:56:04 PM >> Subject: Bimodal compilation > >> Hi all, >> for Devoxx France, me and Jos? Paumard are giving a talk about Valhalla and >> Amber with several benchmarks mixing the two. >> >> One problem we have is that the way pattern matching is compiled by javac and >> later JIT compiled leads to bimodal performance. >> Depending on the day (exactly, depending on the JIT threads scheduling), either >> the method containing the switch is compiled as a whole method (good day) or >> only the method handle (corresponding to the invokedynamic) is compiled and >> when the whole method is compiled, the assembly code corresponding to the >> method handle is considered as too big thus not inlined (bad day). >> >> The example is using arrays of non null instance of value classes with a default >> instance (the kind of value classes that are flattened in memory) and a sealed >> interface, if the method is not fully inlined (good day) performance have very >> good and if not performance are terrible (bad day). >> >> A cascade of instanceof while slightly less fast does not exhibit that issue. >> >> In our example, this bimodal performance issue is hidden when using identity >> classes, because of the cache misses become the bottleneck. >> >> I'm a little worry here because I do not see how to fix this bug without >> changing the way switch on types are compiled by javac, so this problem has to >> be tackled before the JDK 21 is released otherwise, people will have to >> recompile their application to fix that bug. >> >> regards, >> R?mi >> >> --- >> >> public @ZeroDefault @Value record Population(int amount) { >> public static Population zero() { >> return new Population(0); >> } >> public Population add(Population other) { >> return new Population(this.amount + other.amount); >> } >> } >> public sealed interface Populated permits City, Department, Region {} >> public @ZeroDefault @Value record City(String name, @NonNull Population >> population) implements Populated {} >> public @ZeroDefault @Value record Department(String name, City[] cities) >> implements Populated { >> public Department { >> cities = Arrays.stream(cities).toArray(size -> RT.newNonNullArray(City.class, >> size)); >> } >> } >> public @ZeroDefault @Value record Region(String name, Department[] departments) >> implements Populated { >> public Region { >> departments = Arrays.stream(departments).toArray(size -> >> RT.newNonNullArray(Department.class, size)); >> } >> } >> >> public static Population sumPopulationOf(Populated populated) { >> return switch (populated) { >> case City(var name, var population) -> population; >> case Department(var name, var cities) -> { >> var sum = Population.zero(); >> for(var city: cities) { >> sum = sum.add(sumPopulationOf(city)); >> } >> yield sum; >> } >> case Region(var name, var departments) -> { >> var sum = Population.zero(); >> for(var department: departments) { >> sum = sum.add(sumPopulationOf(department)); >> } >> yield sum; >> } >> }; >> } >> >> public static Population sumPopulationOf(Populated[] populateds) { >> var sum = Population.zero(); >> for(var populated: populateds) { >> sum = sum.add(sumPopulationOf(populated)); >> } >> return sum; >> } >> >> --- >> >> public class BenchDOP { >> private Region[] regions; >> >> @Setup >> public void init() { >> var data = Data.readCities(); >> regions = data.regions().toArray(size -> RT.newNonNullArray(Region.class, >> size)); >> } >> >> @Benchmark >> public Population sumPopulations() { >> return Data.sumPopulationOf(regions); >> } >> } >> >> >> --- >> >> >> >> # Benchmark: >> org.paumard.amber.model.cityvaluenonnullablearraydrecords.BenchDOP.sumPopulations >> >> # Run progress: 0.00% complete, ETA 00:02:06 >> # Fork: 1 of 3 >> # Warmup Iteration 1: 47.845 us/op >> # Warmup Iteration 2: 38.199 us/op >> # Warmup Iteration 3: 38.130 us/op >> # Warmup Iteration 4: 37.909 us/op >> # Warmup Iteration 5: 38.345 us/op >> Iteration 1: 38.581 us/op >> Iteration 2: 37.946 us/op >> Iteration 3: 37.837 us/op >> Iteration 4: 38.013 us/op >> Iteration 5: 37.885 us/op >> Iteration 6: 37.853 us/op >> Iteration 7: 37.931 us/op >> Iteration 8: 37.874 us/op >> Iteration 9: 37.828 us/op >> Iteration 10: 37.925 us/op <--- good day >> >> # Run progress: 8.33% complete, ETA 00:02:00 >> # Fork: 2 of 3 >> # Warmup Iteration 1: 2871.011 us/op >> # Warmup Iteration 2: 2761.856 us/op >> # Warmup Iteration 3: 2759.977 us/op >> # Warmup Iteration 4: 2761.045 us/op >> # Warmup Iteration 5: 2756.167 us/op >> Iteration 1: 2755.180 us/op >> Iteration 2: 2781.178 us/op >> Iteration 3: 2759.068 us/op >> Iteration 4: 2755.737 us/op >> Iteration 5: 2755.112 us/op >> Iteration 6: 2754.553 us/op >> Iteration 7: 2761.759 us/op >> Iteration 8: 2750.829 us/op >> Iteration 9: 2751.265 us/op >> Iteration 10: 2749.668 us/op <--- bad day >> >> # Run progress: 16.67% complete, ETA 00:01:48 >> # Fork: 3 of 3 >> # Warmup Iteration 1: 42.359 us/op >> # Warmup Iteration 2: 38.322 us/op >> # Warmup Iteration 3: 38.311 us/op >> # Warmup Iteration 4: 37.990 us/op >> # Warmup Iteration 5: 37.988 us/op >> Iteration 1: 38.139 us/op >> Iteration 2: 38.052 us/op >> Iteration 3: 37.959 us/op >> Iteration 4: 38.037 us/op >> Iteration 5: 37.997 us/op >> Iteration 6: 37.957 us/op >> Iteration 7: 37.977 us/op >> Iteration 8: 37.905 us/op >> Iteration 9: 37.913 us/op >> Iteration 10: 37.976 us/op <--- good day From amaembo at gmail.com Thu Apr 6 06:29:06 2023 From: amaembo at gmail.com (Tagir Valeev) Date: Thu, 6 Apr 2023 08:29:06 +0200 Subject: Expanding the "expression syntax" In-Reply-To: References: Message-ID: This is syntactically ambiguous, as there are already initializers with similar syntax. Also there are code block lambda bodies, so it would require special parsing rules for lambdas. Using do-expressions is unambiguous in every context. On Wed, Apr 5, 2023, 22:34 tom L wrote: > Hello, > > > > This sounds like a cool idea, but why use a keyword (do) at the first > place ? > I read the discussion, which mostly was about the do keyword, but I feel > like there shouldn?t be a keyword at the first place. > > int foo = { > > yield 5 ; > > } > > Blocks already exist, and while they do not return anything currently, you > could make so if there is a yield, it would mean it can return a value > basically. > It?s similar in my opinion to how you can assign the result of a void > method, but you can from any other return type, except that here it?s > inferred. > > It can also be seen as a generalization of a switch statement. > > (Sorry if my vocabulary is off, but I hope you see my point) > > > > Great regards > > > > *From : *Tagir Valeev > *To : *Red IO > *Cc : *amber-dev > *Object :*Re: Expanding the "expression syntax" > > > > Hello! > > > > Just for the record, three years ago I posted a similar, though more > universal proposal, "do expressions": > > > https://mail.openjdk.org/pipermail/amber-spec-experts/2020-March/002046.html > > > > With best regards, > > Tagir Valeev. > > > > On Wed, Apr 5, 2023 at 11:58?AM Red IO > wrote: > > I did some more research and testing and concluded that loop expressions > are more difficult to achieve than I thought and are not as simple as I > thought. > > So I limit my proposal to try expressions since I really think they are > worth it. > > A try expression would follow this syntax: > > TARGET VARIABLE = TRY_KEYWORD [RECOURSE_BLOCK] TRY_BODY(with every path > leading to a yield or throw) [CATCH_BLOCKS(with also a yield or throw in > every path)] > > [FINALLY_BLOCK (with no yield but throw possible)]; > > > > An example would be: > > int fileInt = try (var reader = new BufferedReader(new > FileReader("test.txt"))) { > > var line = reader.readLine(); > > if (line == null) > > yield -1; > > yield Integer.parseInt(line); > > } catch (IOException e) { > > throw new CustomException("Couldn't open file", e); > > } catch (NumberFormatException e) { > > yield -1; > > } finally { > > if (outsideCondition) > > throw new CustomCancelledExecption(); > > System.out.println("file read done"); > > }; > > > > This example is constructed to feature all possible parts of the syntax > and is not necessarily code that makes sense to write. > > > > Great regards > > RedIODev > > > > On Sat, Mar 4, 2023, 10:55 Red IO wrote: > > Currently we have the switch expression as the only expression returning a > result. > > Example: > > boolean b = switch (1) { > > case 0 -> false; > > case 1 -> { > > yield true; > > } > > }; > > > > The idea would be to expand this syntax to different expressions for > example the try expression: > > > > int i = try { > > yield Integer.parseInt("Abc"); > > } catch (NumberFormatException e) { > > yield -1; > > }; > > > > Or loops: > > > > String searched = for(String s : args) { > > if (s.startsWith("-")) > > yield s; > > }; > > > > The idea is to make all java control flow expressions able to yield a > value. > > > > Among many new patterns possible like the examples above it would for > example "clean up" the exception catching initialization: > > > > Foo foo = null; > > > > try { > > foo = new Foo(); > > } catch (SomeException e) { > > //do something or nothing > > } finally { > > //maybe change the value again > > } > > > > To > > > > var foo = try { > > yield new Foo(); > > } catch (SomeException e) { > > //throw or yield > > } finally { > > //throw or do nothing > > }; > > > > Another benefit especially in this example is the clearer state of the > result. In an yielding expression you have to either yield or throw. Also > subsequent manipulation in for example the finally block is not possible > making the source of the returned result clear to identify. > > > > Great regards > > RedIODev > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Fri Apr 7 21:28:32 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Fri, 7 Apr 2023 21:28:32 +0000 Subject: Draft Spec for Pattern Matching for switch (JEP 441) and Record Patterns (JEP 440) now available Message-ID: <74893015-FD27-44AA-84FD-375BC240DF89@oracle.com> Dear experts: The first draft of the joint spec change document for the features Pattern Matching for switch [1] and Record Patterns [2] is now available at: https://cr.openjdk.org/~gbierman/jep440+441/latest/ Please give us your feedback (either on this list or directly to me). Thanks, Gavin [1] Pattern matching for switch: https://openjdk.org/jeps/441 [2] Record patterns: https://openjdk.org/jeps/440 From roberts14 at cablelink.at Sat Apr 8 17:09:18 2023 From: roberts14 at cablelink.at (Robert) Date: Sat, 8 Apr 2023 19:09:18 +0200 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) Message-ID: <2F768D17-88B7-4D71-B31B-AD8C884A1370@cablelink.at> Hi all, newbie here. First off ? loving Amber features. Now, a *very narrow* observation about how JDK 20 handles sealed type selector expressions. (Code snippet below.) If a sealed type S later grows its list of permitted subtypes at some point, how should the compiler handle switch blocks with S as selector? In the absence of a catchall label (i.e. ?case default -> ...? or ?case S s -> ...?) , the compiler already correctly issues an error that coverage is incomplete. But if the block ended with a catchall label, the compiler is silent; the best that can be hoped-for is that the coder throws a defensive exception in the catchall label (and hope that it is triggered in testing). Suggestion: Compiler should *disallows* catchall statements in switch blocks that use a sealed type in the switch selector expression. Note that this special case does not affect all the other non-sealed-type selectors (e.g. Object o), and appears (to me) to strengthen the safety-value of sealed types in this particular scenario (similar to enums). All in Java?s spirit of ?least surprise?. Cheers, Robert PS. reported behavior confirmed using jshell in JDK 20: jshell --enable-preview | Welcome to JShell -- Version 20 | For an introduction type: /help intro on the following: sealed interface S permits S.X, S.Y, S.Z, S.NEWBIE { final class X implements S {}; final class Y implements S {}; final class Z implements S {}; final class NEWBIE implements S {}; public static void main(String [] sa) { S something = new NEWBIE(); System.out.println( switch (something) { case null -> 0; case X x -> 1; case Y y -> 2; case Z z -> 3; case S s -> -1; // case default -> -1; }); }; } -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sat Apr 8 18:41:00 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 8 Apr 2023 18:41:00 +0000 Subject: Some thoughts and suggestions regarding last month's post on Deconstruction Patterns In-Reply-To: References: Message-ID: <591756A3-034D-454D-B7F2-10BD5A8C5509@oracle.com> However, looking at this gives me some pause too, because that means that the code is in our hands to write, for better or for worse. There seems to be no form of constraint placed on these custom deconstruction pattern - just that you have to assign a value to all of the "out-parameters". It?s an overstatement to say ?no constraints?, but the reality is, we can?t constrain away all the things people could do wrong. So yes, there is a risk that people will not follow the rules and write bad deconstruction patterns. Note that we have this problem already with record patterns! Consider record Foo(int x) { Int x() { return 42; } } or record Bar(int x) { Int x() { throw new RuntimeException(); } } The first merely deconstructs the record wrong; the latter ?poisons? any pattern match on it. We can try to eliminate the most egregious errors (e.g., disallow ?throw? statement at the top level) but this is obviously only a bandaid since it can easily be laundered through a method call. The bottom line is that deconstructors and records do have a restricted programming model, and it is on users to code them correctly. Note we had a very similar conversation when we did streams. Given a stream pipeline: ints.stream() .map(x -> { lastX = x; return x + lastX; }) ? If you try to run this in parallel you will not get the answer you expected. Tl:dr; pervasive mutability means we can?t stop people from shooting their feet, we can only guide them to a better programming model and hope they follow. The problem is that deconstructors were meant to be composed. They were meant to be nested. It's that trait that makes this feature so worth it. You can deconstruct complex objects down to just their essentials in an expressive and intuitive way. Yes, composition is powerful, but it magnifies the risk of poison-in, poison-out. And therefore, regardless of how small the chance of a bug is, that chance gets multiplied, not just for each level of decomposition, but for each deconstructor that you write. After all, it's not just about having the tool be correct, but using it correctly. So the chance of failure, regardless of how small, grows exponentially. I get your concern, though I think ?exponentially? is a bit hyperbolic. One suggestion would be the ability to create deconstructors that delegate some of their work to other deconstructors. That?s already in the plan; constructors delegate to super-constructors, and deconstructors are the dual of constructors. Another suggestion would be to create some concept of a "final" for the fields that we are assigning stuff to. Being able to force myself to assign something a value, but also only assign it once, is a powerful construct that already helps me when writing normal code, so I know that it can really help to avoid making some of the same mistakes here and limit the errors in my decomposition. It depends on the exact expression of the deconstructor body (and I don?t want to dive into syntax now), but yes, one rational model here is to treat bindings as blank finals, and require they be definitely assigned before exit. Then we don?t need to ?create some concept of final?, because we can use the finality we already have. Yet another suggestion would be something a little more controversial (not to mention that this ship may have already sailed) -- I'd like the user of the deconstruction pattern to be able to opt-in to forcing their binding variables to CONTAIN the same identifiers that were used at the deconstructing method's signature. Unfortunately, this is just unworkable. Suppose you could this on a record: record R(@ForcedUseSiteNameMatchDammit int x) { } Now, if we construct record Pair(R r1, R r2) { ? } Then no one can use pattern matching on Pair: case Pair(R(int x), R(int x)) // duplicate variable names Now, you said ?contain?, so perhaps you meant something like Case Pair(R(int x1), R(int x2)) But I promise you, no one will thank you for this degree of ?do it because I think it is good for you.? Being able to know the field names Not all patterns will just be unpacking fields. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Apr 8 22:47:36 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 8 Apr 2023 18:47:36 -0400 Subject: Some thoughts and suggestions regarding last month's post on Deconstruction Patterns In-Reply-To: <591756A3-034D-454D-B7F2-10BD5A8C5509@oracle.com> References: <591756A3-034D-454D-B7F2-10BD5A8C5509@oracle.com> Message-ID: Hello, Thank you for the response! > It?s an overstatement to say ?no constraints? but the > reality is, we can?t constrain away all the things people > could do wrong. So yes, there is a risk that people will > not follow the rules and write bad deconstruction > patterns. > > Note that we have this problem already with record > patterns! Consider > > record Foo(int x) { > Int x() { return 42; } > } > > or > > record Bar(int x) { > Int x() { throw new RuntimeException(); } > } > > The first merely deconstructs the record wrong; the > latter ?poisons? any pattern match on it. Apologies, I could have definitely used better words than "no constraints". And like you said, there also exists some of the same vulnerabilities within record patterns, such as badly written getters poisoning the patterns. That said, the record gives you a deconstructor as a freebie. If I do nothing but model my state correctly in a record, then I get a deconstructor that is guaranteed to work. I'm not saying that to say, I want a deconstructor for free for plain classes too. I say that to explain why I hesitate to be as trigger happy as I was with record patterns, but for plain class deconstruction patterns instead. In fact, looking back at the past few months, I'm noticing my mindset starting to shift to using records and enums by default, only going to classes when necessary or best to. Records are becoming more and more potent to me as things move forward. I feel like I did not appreciate their value when I first saw them, so much so that now, I don't like the prospect of going back to a class when I need to. Lol, I guess a more accurate way of wording my original post would have been "I'm scared to be without my freebie safety net, so here are some ideas that might help me feel a bit better," childish as it may sound. > We can try to eliminate the most egregious errors (e.g., > disallow ?throw? statement at the top level) Oh, that's a really good idea. And the reason why we can do that is because the goal of decomposition is to break out the parts we want from the object, then do something meaningful with them. If we don't like what we see in one of the parts, we should throw that exception AFTER we broke out the part we want, ran it against some check, and then decided that it is worth throwing an exception for. Not during the decomposition itself. Am I understanding that right? > but this is obviously only a bandaid since it can easily > be laundered through a method call. The bottom line is > that deconstructors and records do have a restricted > programming model, and it is on users to code them > correctly. I see that now. Ultimately, this gift has a price tag and we need to pay, regardless of what support the language provides for us. > Note we had a very similar conversation when we did > streams. Given a stream pipeline: > > ints.stream() > .map(x -> { lastX = x; return x + lastX; }) > ... > > If you try to run this in parallel you will not get the > answer you expected. > > Tl:dr; pervasive mutability means we can?t stop people > from shooting their feet, we can only guide them to a > better programming model and hope they follow. I think I get what you are saying. And if so, then it makes good sense to me and I agree. The above code is illegal now because you all decided not to allow any outside object into the lambda that wasn't effectively final. However, using some indirection (encapsulate lastX into an object and replace the operators with getters and setters), I can end up with essentially the same problem as above. So, you all put the check on the first level (only allow effectively final objects into the lambda), and leave the rest into the hands of the coders because it really is their responsibility to write good code in the first place - you can only help so much. Am I understanding that right? > Yes, composition is powerful, but it magnifies the risk > of poison-in, poison-out. > > I get your concern, though I think ?exponentially? is a > bit hyperbolic. Fair, quantifying it isn't feasible anyways. And regardless, my fears are mostly dissuaded since you then said the following. > > One suggestion would be the ability to create > > deconstructors that delegate some of their work to > > other deconstructors. > > That?s already in the plan; constructors delegate to > super-constructors, and deconstructors are the dual of > constructors. > > > Another suggestion would be to create some concept of a > > "final" for the fields that we are assigning stuff to. > > Being able to force myself to assign something a value, > > but also only assign it once, is a powerful construct > > that already helps me when writing normal code, so I > > know that it can really help to avoid making some of > > the same mistakes here and limit the errors in my > > decomposition. > > It depends on the exact expression of the deconstructor > body (and I don?t want to dive into syntax now), but yes, > one rational model here is to treat bindings as blank > finals, and require they be definitely assigned before > exit. Then we don?t need to ?create some concept of > final?, because we can use the finality we already have. Happy to hear that these are viable options. And thanks for explaining how finality might be done for the bindings of deconstruction patterns. It's nice when the tools we have can be used in more places than you expect. It's one of the things I like about Java. That said, to consider the needs of other developers, it might be nice if that finality was something that we could opt in to. Or even better, opt out of. > > Yet another suggestion would be something a little more > > controversial (not to mention that this ship may have > > already sailed) -- I'd like the user of the > > deconstruction pattern to be able to opt-in to forcing > > their binding variables to CONTAIN the same identifiers > > that were used at the deconstructing method's > > signature. > > Unfortunately, this is just unworkable. Suppose you could > this on a record: > > record R(@ForcedUseSiteNameMatchDammit int x) { } > > Now, if we construct > > record Pair(R r1, R r2) { ? } > > Then no one can use pattern matching on Pair: > > case Pair(R(int x), R(int x)) > // duplicate variable names > > Now, you said ?contain?, so perhaps you meant something like > > Case Pair(R(int x1), R(int x2)) > > But I promise you, no one will thank you for this degree of ?do it because I think it is good for you.? All fair points, especially the ones after the "contain". A lot of this came out of fears that have since been addressed and dealt with. Not to mention I now agree with you. > > Being able to know the field names > > Not all patterns will just be unpacking fields. Oh right, that is true. It's sometimes hard to remember that I am not just decomposing objects into their components, but I am now decomposing the objects into whatever legal representation of their state should be. I could decompose a BigFraction (composed of a numerator BigInteger and a denominator BigInteger) into a BigDecimal, which would represent the decimal form of the fraction. Or even just to 2 instances of java.lang.Long, for the numerator and denominator. That also helps me better understand what we are getting with this open-endedness. There's a lot of flexible ways that we can decompose objects, which is why we have to give up the simplicity in order to get that flexibility. Thank you for helping me out! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Apr 8 23:27:42 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 8 Apr 2023 19:27:42 -0400 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) Message-ID: Hello Robert, I am not a part of the Amber Dev Team, but I have some thoughts. > If a sealed type S later grows its list of permitted > subtypes at some point, how should the compiler handle > switch blocks with S as selector? > > In the absence of a catchall label > > (i.e. ?case default -> ...? or ?case S s -> ...?) > > the compiler already correctly issues an error that > coverage is incomplete. > > But if the block ended with a catchall label, the > compiler is silent; the best that can be hoped-for is > that the coder throws a defensive exception in the > catchall label (and hope that it is triggered in > testing). > > Suggestion: > > Compiler should *disallows* catchall statements in switch > blocks that use a sealed type in the switch selector > expression. Note that this special case does not affect > all the other non-sealed-type selectors (e.g. Object o), > and appears (to me) to strengthen the safety-value of > sealed types in this particular scenario (similar to > enums). > > All in Java?s spirit of ?least surprise?. Wow, great catch. This is much trickier than it seems to be at a first glance. You want to make sure that a developer doesn't accidentally shoot themselves in the foot when putting the sealed type itself into the switch expression when the object being compared against is already a sealed type. The reason why is that the reader might be fooled into thinking that any new additions into the sealed type will trigger an exception here, when in reality, the inclusion of the sealed type itself as a case is basically synonymous with a default clause BECAUSE THE TYPE OF THE PARAMETER TO THE SWITCH IS THE SEALED TYPE ITSELF. Narrow is a good word to use here lol. And I think this suggestion definitely supports Java's idea of least surprise. I would have fallen for this for sure. I agree with you, this should probably throw an error. But it definitely should at least give a warning. If your switch expression is longer than a few lines and the permitted subclasses of the sealed type resemble the sealed type in name, then this can be a very easy pothole to fall into. Thank you for pointing this out! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 9 00:27:36 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sat, 8 Apr 2023 20:27:36 -0400 Subject: Introduced by a guard? (Question on Draft Spec for JEP 440 and 441) Message-ID: Hello Amber Dev Team, Thank you Gavin for posting the spec! > 6.3.4 Scope for Pattern Variables in Switch Labels > > Pattern variables can be introduced by case labels with a > case pattern, either by the pattern itself or by a guard, > and are in scope for the relevant parts of the associated > switch expression (6.3.1.6) or switch statement (6.3.2.6) > > The following rules apply to case labels: > > * A pattern variable is introduced by a case label > with a case pattern p if it is declared by p. > > * A pattern variable declared by the pattern of a > guarded case pattern is definitely matched in the > associated guard. > > - It is a compile-time error if any pattern > variable declared by the pattern of a guarded > case pattern is already in scope at its guard. > > * A pattern variable is introduced by a guarded case > label if it is introduced by the associated guard > when true (6.3.1). Reading the bullets here (especially the final bullet point) implies to me that the only time that the guard of a guarded case pattern can introduce variables is when the guard contains a pattern match itself. So, something like this. Number input = 4; String response = switch (input) { case Number n when n instanceof Integer i -> String.valueOf(i); default -> "idc"; }; Apologies for the terrible example. However, when reading the sentences in the quoted text above the bullets, they seem a lot more loose. More specifically, reading that text makes me think something like this is possible (obviously illegal and nonsensical code, but trying to make a point). switch (someObject) { case Integer i when (int abc = method()) == i -> "???"; default -> "idk"; }; Apologies for the terrible example. I guess my question is, is there another way besides patterns for a variable to be introduced in the guard? And if not, would it make more sense to instead say something like the following instead? > Pattern variables can be introduced by case labels with a > case pattern, either by the pattern itself or by a guard > containing a pattern, and are in scope for the relevant > parts of the associated switch expression (6.3.1.6) or > switch statement (6.3.2.6) Apologies if this is just noise, but I wanted to bring it up because I initially parsed the quoted passage to imply the second code example I gave. It wasn't until several minutes of reading and screwing around in jshell did I start to think the passage was actually implying the first code example I gave. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Sun Apr 9 10:44:12 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Sun, 9 Apr 2023 13:44:12 +0300 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) In-Reply-To: <2F768D17-88B7-4D71-B31B-AD8C884A1370@cablelink.at> References: <2F768D17-88B7-4D71-B31B-AD8C884A1370@cablelink.at> Message-ID: I'm not sure I understand the problem you present, are you saying that if I have the following (I'm on my phone, so I apologize about the formatting): sealed interface S permits A {} switch(x) { case A a -> ... case S s -> ... } Then when I modify S to be: sealed interface S permits A, B {} My switch expression now has a hidden case? I.e. the compiler doesn't help me know that I may want to add an explicit B case? I can see few problems with disallowing a "catchall" clause. Here is a textbook example of sealed types: sealed interface Json permits JPrimitive, JArray, JObject {} sealed interface JPrimitive extends Json permits JNull, JBool, JNumber, JString { T unbox(); } // the implementation of unbox just returns the underlying object, for JNull it returns null. record JNull() implements JPrimitive {} record JBool(bool val) implements JPrimitive {} ... Will: switch(x) { JArray a -> ... JObject o -> ... JPrimitive p -> ... } Be allowed? It has the same problem, if we extend the permits list of JPrimitive, the compiler won't notify you, but we really want a single "primitive" clause, semantically we know that it won't ever change (but the compiler has no way of knowing it). If you only care about the "first layer", there are still cases were you have a sealed types that permits quite a lot of types (say, 10, which is a lot but possible), you may have a function that receive that type, and handle 7 out of the 10 ways the same way, but 3 of the types require a special handling (think subtypes that requires external resources, or thread locks), not having a catchall will create a lot of boilerplate (or will make you switch to if-else chain (pun intended)) --- If you still think it is a problem, I'm sure you could make a rule in your linter/build pipeline to disallow catchall clauses On Sat, Apr 8, 2023, 21:24 Robert wrote: > Hi all, newbie here. > > First off ? loving Amber features. > > Now, a *very narrow* observation about how JDK 20 handles sealed type > selector expressions. > (Code snippet below.) > > If a sealed type S later grows its list of permitted subtypes at some > point, > how should the compiler handle switch blocks with S as selector? > > In the absence of a catchall label (i.e. ?case default -> ...? or ?case S > s -> ...?) , > the compiler already correctly issues an error that coverage is incomplete. > > But if the block ended with a catchall label, the compiler is silent; the > best that > can be hoped-for is that the coder throws a defensive exception in the > catchall label > (and hope that it is triggered in testing). > > Suggestion: > > Compiler should *disallows* catchall statements in switch blocks that use > a sealed type > *in the switch selector expression*. Note that this special case does > not affect all the > other non-sealed-type selectors (e.g. Object o), and appears (to me) to > strengthen the > safety-value of sealed types in this particular scenario (similar to > enums). > > All in Java?s spirit of ?least surprise?. > > Cheers, > Robert > > > PS. reported behavior confirmed using jshell in JDK 20: > > jshell --enable-preview > | Welcome to JShell -- Version 20 > | For an introduction type: /help intro > > > on the following: > > sealed interface S permits S.X, S.Y, S.Z, S.NEWBIE { > > final class X implements S {}; > > final class Y implements S {}; > > final class Z implements S {}; > > final class NEWBIE implements S {}; > > public static void main(String [] sa) { > > S something = new NEWBIE(); > System.out.println( > switch (something) { > case null -> 0; > case X x -> 1; > case Y y -> 2; > case Z z -> 3; > case S s -> -1; > // case default -> -1; > }); > }; > } > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 9 15:08:34 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 9 Apr 2023 11:08:34 -0400 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) Message-ID: Hello, > I'm not sure I understand the problem you present, are > you saying that if I have the following (I'm on my phone, > so I apologize about the formatting): > > sealed interface S permits A {} > > switch(x) { > case A a -> ... > case S s -> ... > } > > Then when I modify S to be: > > sealed interface S permits A, B {} > > My switch expression now has a hidden case? I.e. the > compiler doesn't help me know that I may want to add an > explicit B case? Well, not necessarily. By definition, case S s and the default clause hide new inclusions into the type domain. That is their job and we are not trying to take away this functionality. The problem is that there are a handful of situations where case S s and default are 1-to-1 matches in matched values. This is a very tricky problem in my eyes because it took me a long time of looking at Robert's example before I realized that the default clause and the case S s are subsets of each other, which is to say they cover the exact same cases - no more and no less. > I can see few problems with disallowing a "catchall" clause. > > Here is a textbook example of sealed types: > > sealed interface Json permits JPrimitive, JArray, JObject {} > > sealed interface JPrimitive extends Json permits JNull, JBool, JNumber, > JString { > T unbox(); > } > > // the implementation of unbox just returns the underlying object, for > JNull it returns null. > record JNull() implements JPrimitive {} > record JBool(bool val) implements JPrimitive {} > ... > > Will: > > switch(x) { > JArray a -> ... > JObject o -> ... > JPrimitive p -> ... > } > > Be allowed? It has the same problem, if we extend the permits list of > JPrimitive, the compiler won't notify you, but we really want a single > "primitive" clause, semantically we know that it won't ever change (but the > compiler has no way of knowing it). So to be clear, the part that Robert is pointing out is that this only applies in a situation where the switch selector expression returns the type S, which is the sealed type. So in the example that you provided, the type of the selector expression would have to be JPrimitive for the situation that Robert is describing to apply (though you address this later). > If you only care about the "first layer", there are still > cases were you have a sealed types that permits quite a > lot of types (say, 10, which is a lot but possible), you > may have a function that receive that type, and handle 7 > out of the 10 ways the same way, but 3 of the types > require aspecial handling (think subtypes that requires > external resources, or thread locks), not having a > catchall will create a lot of boilerplate (or will make > you switch to if-else chain (pun intended)) So I think I understand what you mean by first layer, but I will dive into an example anyways to be explicit. Let's rework your first example to be exactly what we need -- a sealed type with 10 permitted subclasses. Also, we will apply the restriction that Robert mentioned -- the "case S s" must also be the type of the switch selector expression. sealed interface S permits A, B, C, D, E, F, G, H, I, J { /** Methods, etc. */ } record A(long a) implements S { /** Methods, etc. */ } record B(int b) implements S { /** Methods, etc. */ } record C(short c) implements S { /** Methods, etc. */ } record D(String d) implements S { /** Methods, etc. */ } /** Record E and the rest. */ final S sealedType = new D("idc"); Ok, now we have the foundation we need to make examples. And let me repeat this part of your quote specifically. > you may have a function that receive that type (S), and > handle 7 out of the 10 ways the same way, but 3 of the > types require a special handling (think subtypes that > requires external resources, or thread locks), not having > a catchall will create a lot of boilerplate (or will make > you switch to if-else chain (pun intended)) Well, let's start off by making sure we accurately capture what you are saying final SomeType output = switch (sealedType) //from earlier { case A a -> { /** Some complex logic */ } case B b -> { /** Some complex logic */ } case C c -> { /** Some complex logic */ } case S s -> { /** Do something with s that handles the remaining 7 cases all the same way */ } }; Am I capturing your point correctly here? Because if so, why not just do this instead? final SomeType output = switch (sealedType) //from earlier { case A a -> { /** Some complex logic */ } case B b -> { /** Some complex logic */ } case C c -> { /** Some complex logic */ } default -> { /** Do something with sealedType that handles the remaining 7 cases all the same way */ } }; Remembed, both (s) and (sealedType) have the type S. So really, anything that we can use (s) for, we can 100% of the time replace it with (sealedType) assuming no when clauses or other fluff. That's the point that Robert is getting at here. There is no situation that could be represented with just case S s by itself that could not also be captured with default when the switch selector expression is of type S. Both of these situations are subsets of each other, they cover 100% of the exact same cases. And therefore, he is proposing that we do something when this occurs. He suggests error, I suggest warning. And the error/warning should tell you to use a default clause instead. > If you still think it is a problem, I'm sure you could > make a rule in your linter/build pipeline to disallow > catchall clauses Can you think of any examples where you would want to have a case S s (excluding when clauses of course) that could not be better communicated and handled with a default clause and using the existing sealedType variable? If there aren't any other cases, then both Robert and I think that this ambiguity should be prevented. The purpose of the default clause is to make it LOUD and clear to the reader that all cases left are to be captured under this clause. To instead use case S s can provide just enough indirection such that if there is enough noise above the switch expression, then this is a pretty easy pothole to miss. And based on that, we think that this should be some sort of warning/error on the language level. Not just for some third party linter. Thank you for your time and insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Apr 9 18:18:49 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 9 Apr 2023 18:18:49 +0000 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) In-Reply-To: <2F768D17-88B7-4D71-B31B-AD8C884A1370@cablelink.at> References: <2F768D17-88B7-4D71-B31B-AD8C884A1370@cablelink.at> Message-ID: I understand why you were motivated to make this suggestion. But, I think the restriction you propose is overly restrictive. First let me amplify on your motivation, as this was an important motivation in the design of this feature: a switch that is exhaustive without a catch-all case is better than a switch that is exhaustive with one. This is not because you don?t have to write the catch all case when you cover all the cases, though that is a nice side benefit. It is because you get _better type checking_. The compiler verifies your intent of exhaustiveness, and triggers an error if this is undermined by later additions to an enum or sealed class. A default sweeps the sin under the rug. So, you say, if better type checking is better, why not force everyone to get that better type checking by being exhaustive the honest way? This is where things start to go off; there are too many legitimate use cases that this excludes. First, consider the case where you have a large set of choices (such as tokens in a parser), but you only want to act on a few of them. Switch is a good tool for this: status = switch (nextToken) { case OPEN_PAREN -> ? case IDENTIFIER -> ? default -> throw new ParseException(?Expecting identifier or parentheses?); } This code is fine! It is clear what is going on ? we?re expecting one of two tokens, and anything else is an error. Is the user helped by being required to list the other 72 tokens explicitly instead of a catch-all? Is the code made more readable? Another example is the common pattern of a switch with two clauses, which is often more appropriate than an if: boolean shouldStop = switch (color) { case RED -> true; default -> false; } Yes, this could be a ternary, but this pattern is pretty useful and pretty clear, and ternaries don?t scale. Having to list out YELLOW and GREEN in the second case, even though there are only two, would not be an improvement. I think what tripped you up was you extrapolated from a set of examples where you really were handling all, or nearly all, of the cases. But switches are useful when you only want to handle one or two cases specially too. On Apr 8, 2023, at 10:09 AM, Robert > wrote: Hi all, newbie here. First off ? loving Amber features. Now, a *very narrow* observation about how JDK 20 handles sealed type selector expressions. (Code snippet below.) If a sealed type S later grows its list of permitted subtypes at some point, how should the compiler handle switch blocks with S as selector? In the absence of a catchall label (i.e. ?case default -> ...? or ?case S s -> ...?) , the compiler already correctly issues an error that coverage is incomplete. But if the block ended with a catchall label, the compiler is silent; the best that can be hoped-for is that the coder throws a defensive exception in the catchall label (and hope that it is triggered in testing). Suggestion: Compiler should *disallows* catchall statements in switch blocks that use a sealed type in the switch selector expression. Note that this special case does not affect all the other non-sealed-type selectors (e.g. Object o), and appears (to me) to strengthen the safety-value of sealed types in this particular scenario (similar to enums). All in Java?s spirit of ?least surprise?. Cheers, Robert PS. reported behavior confirmed using jshell in JDK 20: jshell --enable-preview | Welcome to JShell -- Version 20 | For an introduction type: /help intro on the following: sealed interface S permits S.X, S.Y, S.Z, S.NEWBIE { final class X implements S {}; final class Y implements S {}; final class Z implements S {}; final class NEWBIE implements S {}; public static void main(String [] sa) { S something = new NEWBIE(); System.out.println( switch (something) { case null -> 0; case X x -> 1; case Y y -> 2; case Z z -> 3; case S s -> -1; // case default -> -1; }); }; } -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 9 18:44:58 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 9 Apr 2023 14:44:58 -0400 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) Message-ID: Hello Brian, I think you may have misinterpreted the original post. Please see my response here for more details. https://mail.openjdk.org/pipermail/amber-dev/2023-April/007986.html The original post is not saying "Why are we allowed to do catch-all's? Isn't that bad?" The original post is saying "There are 2 ways to perform a catch-all, EVEN WHEN THEY COVER THE EXACT SAME CASES, but one of them can be deceptive and easy to misinterpret, while the other is literally built to be a catch all. Can we turn that into an error?" -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 9 18:54:22 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 9 Apr 2023 14:54:22 -0400 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) In-Reply-To: References: Message-ID: Well, correction. I think *I* am the one who misinterpreted the post, but I think an interesting situation occurs if you change the original post to be "Don't allow any catch-all" to be "Don't allow case S s as a catch-all when there already exists a good default and the selector expression is already of type S" -------------- next part -------------- An HTML attachment was scrubbed... URL: From robbepincket at live.be Mon Apr 10 11:27:22 2023 From: robbepincket at live.be (Robbe Pincket) Date: Mon, 10 Apr 2023 11:27:22 +0000 Subject: Named record patterns Message-ID: Hi all I was wondering if named record patterns `o instanceof Pair(String left, String right) pair` are still on the table for the future or not. (Not sure if 'named' record pattern is the right name?) IIRC they were shelfed due to the ambiguity that they could cause in this case, if there also exists a function `when`: ``` switch(o) { case Pair(String left, String right) when when (left == right) -> ... } ``` It feels weird to me however that this feature was shelved just for this case, when a 'simple' rule could be introduced that a record pattern can't introduce a pattern variable with the name of a contextual keyword or at least not `when`. Are there other reasons that record patterns can't introduce a variable that matches the whole pair anymore? It feels like it could be useful, even more so with JEP 443: Unnamed Patterns and Variables (Preview) ``` if (o instanceof ColoredPoint(Point(_, int y), _) cp && y > 0) { doAction(cp); } ``` Instead of ``` if (o instanceof ColoredPoint(Point(int x, int y), Color c) && y > 0) { doAction(new ColoredPoint(new Point(x, y), c)); } ``` or this, if ColoredPoint can both hold `Point` and `Point3D` ``` if (o instanceof ColoredPoint cp && cp.point() instanceof Point(_, int y) && y > 0) { doAction(cp); } ``` or if it can only hold `Point` ``` if (o instanceof ColoredPoint cp && cp.point().y() > 0) { doAction(cp); } ``` Greetings Robbe Pincket -------------- next part -------------- An HTML attachment was scrubbed... URL: From mark.reinhold at oracle.com Mon Apr 10 17:37:01 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Mon, 10 Apr 2023 17:37:01 +0000 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) Message-ID: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> https://openjdk.org/jeps/445 Summary: Evolve the Java language so that students can write their first programs without needing to understand language features designed for large programs. Far from using a separate dialect of Java, students can write streamlined declarations for single-class programs and then seamlessly expand their programs to use more advanced features as their skills grow. This is a preview language feature. - Mark From attila.kelemen85 at gmail.com Mon Apr 10 18:00:26 2023 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 10 Apr 2023 20:00:26 +0200 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: Hi, While I like the idea of making the language more beginner friendly, I don't think the proposal in this form is the best way, because it does exactly what it claims to want to avoid: It introduces a separate beginner's dialect. That is, a form of writing code only to be used by beginners and never again. It is even stranger that the alternatives lists "Interpret code units as static members" (which - in my opinion - would be better) and claims it to be a "distinct Java dialect", despite the fact that this would only mean that the "static" is implicit, and such implicit modifier differences would not be without precedent in the language, yet nobody claims that those are different dialects. Attila Mark Reinhold ezt ?rta (id?pont: 2023. ?pr. 10., H, 19:37): > > https://openjdk.org/jeps/445 > > Summary: Evolve the Java language so that students can write their > first programs without needing to understand language features designed > for large programs. Far from using a separate dialect of Java, students > can write streamlined declarations for single-class programs and then > seamlessly expand their programs to use more advanced features as their > skills grow. This is a preview language feature. > > - Mark From brian.goetz at oracle.com Mon Apr 10 18:17:31 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 10 Apr 2023 18:17:31 +0000 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: > While I like the idea of making the language more beginner friendly, I > don't think the proposal in this form is the best way, because it does > exactly what it claims to want to avoid: It introduces a separate > beginner's dialect. That is, a form of writing code only to be used by > beginners and never again. Since you provide no argument or examples to support your assertion, it is pretty hard to tell where you?ve gone off the trail here. But your claim that the features here are useful only to beginners and never again is simply incorrect. If you want to unpack your claim, perhaps we can clarify the disconnect. From zjx001202 at gmail.com Mon Apr 10 18:34:12 2023 From: zjx001202 at gmail.com (Glavo) Date: Tue, 11 Apr 2023 02:34:12 +0800 Subject: Fwd: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: ---------- Forwarded message --------- From: Glavo Date: Tue, Apr 11, 2023 at 2:25?AM Subject: Re: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) To: amber-dev at openjdk.org Cc: Ron Pressler , amber-dev at openjdk.org < amber-dev at openjdk.org>, jdk-dev at openjdk.org I like some parts of this proposal, but I disagree with it. I think this proposal is too narrow and limits its usefulness. Is the implicit public and static modifiers strange? The interface has worked this way for many years, why is it not acceptable here? Personally, I'd prefer not to allow instance methods to be main methods, and implicitly treat top-level variable and method declarations as static and public -- just like interfaces do. I think this could be a step towards package-level declarations, whereas the current proposal limits the direction it can go. On Tue, Apr 11, 2023 at 1:37?AM Mark Reinhold wrote: > https://openjdk.org/jeps/445 > > Summary: Evolve the Java language so that students can write their > first programs without needing to understand language features designed > for large programs. Far from using a separate dialect of Java, students > can write streamlined declarations for single-class programs and then > seamlessly expand their programs to use more advanced features as their > skills grow. This is a preview language feature. > > - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From ice1000kotlin at foxmail.com Mon Apr 10 21:52:54 2023 From: ice1000kotlin at foxmail.com (=?utf-8?B?VGVzbGEgWmhhbmc=?=) Date: Mon, 10 Apr 2023 17:52:54 -0400 Subject: Apply markdown formatting to the GitHub docs README Message-ID: Hi Amber developers, I have created a pull request on GitHub https://github.com/openjdk/amber-docs/pull/17 to apply markdown formatting for the readme file, and it's getting no attention for almost a year. I wish that this can be merged :) if I am missing something in the PR, please let me know! Best regards, Tesla -------------- next part -------------- An HTML attachment was scrubbed... URL: From ice1000kotlin at foxmail.com Mon Apr 10 21:47:23 2023 From: ice1000kotlin at foxmail.com (=?utf-8?B?VGVzbGEgWmhhbmc=?=) Date: Mon, 10 Apr 2023 17:47:23 -0400 Subject: Fwd: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: > not to allow instance methods to be main methods, and implicitly treat top-level variable and method declarations as static and public -- just like interfaces do.  Strongly agree. Best regards, Tesla ------------------ Original ------------------ From: "Glavo" From forax at univ-mlv.fr Mon Apr 10 22:03:59 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 11 Apr 2023 00:03:59 +0200 (CEST) Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: <1004828670.33494203.1681164239653.JavaMail.zimbra@univ-eiffel.fr> > From: "Tesla Ice Zhang" > To: "Glavo" , "amber-dev" > Sent: Monday, April 10, 2023 11:47:23 PM > Subject: Re:Fwd: New candidate JEP: 445: Flexible Main Methods and Anonymous > Main Classes (Preview) >> not to allow instance methods to be main methods, and implicitly treat top-level >> variable and method declarations as static and public -- just like interfaces > > do. > Strongly agree. > Best regards, > Tesla Sadly, it's not how interfaces work. Interface does not make method declarations implicitly static, only field declarations are implicitly static. R?mi > ------------------ Original ------------------ > From: "Glavo" ; > Date: Tue, Apr 11, 2023 02:34 AM > To: "amber-dev"; > Subject: Fwd: New candidate JEP: 445: Flexible Main Methods and Anonymous Main > Classes (Preview) > ---------- Forwarded message --------- > From: Glavo < [ mailto:zjx001202 at gmail.com | zjx001202 at gmail.com ] > > Date: Tue, Apr 11, 2023 at 2:25 AM > Subject: Re: New candidate JEP: 445: Flexible Main Methods and Anonymous Main > Classes (Preview) > To: [ mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] < [ > mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] > > Cc: Ron Pressler < [ mailto:ron.pressler at oracle.com | ron.pressler at oracle.com ] > >, [ mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] < [ > mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] >, [ > mailto:jdk-dev at openjdk.org | jdk-dev at openjdk.org ] < [ > mailto:jdk-dev at openjdk.org | jdk-dev at openjdk.org ] > > I like some parts of this proposal, but I disagree with it. I think this > proposal is too narrow and limits its usefulness. > Is the implicit public and static modifiers strange? The interface has worked > this way for many years, why is it not acceptable here? > Personally, I'd prefer not to allow instance methods to be main methods, and > implicitly treat top-level variable and method declarations as static and > public -- just like interfaces do. > I think this could be a step towards package-level declarations, whereas the > current proposal limits the direction it can go. > On Tue, Apr 11, 2023 at 1:37 AM Mark Reinhold < [ > mailto:mark.reinhold at oracle.com | mark.reinhold at oracle.com ] > wrote: >> [ https://openjdk.org/jeps/445 | https://openjdk.org/jeps/445 ] >> Summary: Evolve the Java language so that students can write their >> first programs without needing to understand language features designed >> for large programs. Far from using a separate dialect of Java, students >> can write streamlined declarations for single-class programs and then >> seamlessly expand their programs to use more advanced features as their >> skills grow. This is a preview language feature. >> - Mark -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Apr 10 23:24:24 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 10 Apr 2023 23:24:24 +0000 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: As you allude to, there is already the ?Single File Source Launcher?, which let?s you run a Java program directly from the command line without compilation (`java Foo.java`), which has been around for a while, and additionally does support ?shebang? execution. (There are plans to extend this further.). This was aimed at script-like programs, which, while not written by beginners, also do not require the ceremony of the class declaration. So the design center here is not ?beginning programmers?, but ?simple programs?, but of course there is a lot of intersection between the two ? beginning programmers write simple programs. There is also a chicken-and-egg problem here; one of the reasons people don?t write script-like programs in Java so often is because of the declaration and building overhead. But that?s an accidental consequence, and we can remove these impediments. So students may be the most obvious group of beneficiaries, but they are not the only ones. > On Apr 10, 2023, at 11:45 AM, Attila Kelemen wrote: > > Sure, sorry, didn't want to elaborate too long, and lose the main > message (thus only the assertion), but here is my problem in more > detail: > > The reason why I don't think it would be useful in general, because it > only makes writing entry points easier, which would give more > incentive to "#!/bin/java" (correct me if I'm wrong, but I vaguely > remember that there is a JEP for java to auto compile java files and > run them), but I find that to be optimistic, since if somebody wants > something like that, the small extra boilerplate won't stop them. So, > basically this JEP provides a minimal simplification of writing an > entry point, and entry points are rare (compared to the rest of the > code). This is merely my opinion, but writing out the small boiler > plate for the entry point didn't bother me at all. Thus, I would say > that this JEP provides little benefit to anyone but beginners. Or > rather, I would throw back the question: What could I gain from using > this? What value do I get from this as a non-beginner? (not trying to > mock you, I'm honestly curious what am I missing here) I did see you > mention on another list that abstract classes could have a main > method, but if we want such a thing, that need not be tied to this > JEP, and we shouldn't prevent future possibilities by introducing this > JEP (such as easier writing of utility classes). > > Brian Goetz ezt ?rta (id?pont: 2023. ?pr. > 10., H, 20:17): >> >>> While I like the idea of making the language more beginner friendly, I >>> don't think the proposal in this form is the best way, because it does >>> exactly what it claims to want to avoid: It introduces a separate >>> beginner's dialect. That is, a form of writing code only to be used by >>> beginners and never again. >> >> Since you provide no argument or examples to support your assertion, it is pretty hard to tell where you?ve gone off the trail here. >> >> But your claim that the features here are useful only to beginners and never again is simply incorrect. If you want to unpack your claim, perhaps we can clarify the disconnect. >> >> From attila.kelemen85 at gmail.com Mon Apr 10 18:45:40 2023 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 10 Apr 2023 20:45:40 +0200 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: Sure, sorry, didn't want to elaborate too long, and lose the main message (thus only the assertion), but here is my problem in more detail: The reason why I don't think it would be useful in general, because it only makes writing entry points easier, which would give more incentive to "#!/bin/java" (correct me if I'm wrong, but I vaguely remember that there is a JEP for java to auto compile java files and run them), but I find that to be optimistic, since if somebody wants something like that, the small extra boilerplate won't stop them. So, basically this JEP provides a minimal simplification of writing an entry point, and entry points are rare (compared to the rest of the code). This is merely my opinion, but writing out the small boiler plate for the entry point didn't bother me at all. Thus, I would say that this JEP provides little benefit to anyone but beginners. Or rather, I would throw back the question: What could I gain from using this? What value do I get from this as a non-beginner? (not trying to mock you, I'm honestly curious what am I missing here) I did see you mention on another list that abstract classes could have a main method, but if we want such a thing, that need not be tied to this JEP, and we shouldn't prevent future possibilities by introducing this JEP (such as easier writing of utility classes). Brian Goetz ezt ?rta (id?pont: 2023. ?pr. 10., H, 20:17): > > > While I like the idea of making the language more beginner friendly, I > > don't think the proposal in this form is the best way, because it does > > exactly what it claims to want to avoid: It introduces a separate > > beginner's dialect. That is, a form of writing code only to be used by > > beginners and never again. > > Since you provide no argument or examples to support your assertion, it is pretty hard to tell where you?ve gone off the trail here. > > But your claim that the features here are useful only to beginners and never again is simply incorrect. If you want to unpack your claim, perhaps we can clarify the disconnect. > > From omniprof at gmail.com Tue Apr 11 00:00:31 2023 From: omniprof at gmail.com (Ken Fogel) Date: Tue, 11 Apr 2023 00:00:31 +0000 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: As someone who has lamented the decorations necessary to say Hello World, I am excited about this JEP. Python is eating our lunch because it supports the stream of consciousness Design pattern, make it work and then maybe make it nice. That is not to say this simplification for new users won?t lead to the same poor design pattern, a good instructor or good book can point out that this new style is about understanding syntax and then methods. From here you can move to OOP or Functional in Java. I applaud this new JEP. Ken Fogel ________________________________ From: jdk-dev on behalf of Brian Goetz Sent: Tuesday, April 11, 2023 7:24:24 AM To: Attila Kelemen Cc: amber-dev at openjdk.org ; Ron Pressler ; jdk-dev at openjdk.org Subject: Re: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) As you allude to, there is already the ?Single File Source Launcher?, which let?s you run a Java program directly from the command line without compilation (`java Foo.java`), which has been around for a while, and additionally does support ?shebang? execution. (There are plans to extend this further.). This was aimed at script-like programs, which, while not written by beginners, also do not require the ceremony of the class declaration. So the design center here is not ?beginning programmers?, but ?simple programs?, but of course there is a lot of intersection between the two ? beginning programmers write simple programs. There is also a chicken-and-egg problem here; one of the reasons people don?t write script-like programs in Java so often is because of the declaration and building overhead. But that?s an accidental consequence, and we can remove these impediments. So students may be the most obvious group of beneficiaries, but they are not the only ones. > On Apr 10, 2023, at 11:45 AM, Attila Kelemen wrote: > > Sure, sorry, didn't want to elaborate too long, and lose the main > message (thus only the assertion), but here is my problem in more > detail: > > The reason why I don't think it would be useful in general, because it > only makes writing entry points easier, which would give more > incentive to "#!/bin/java" (correct me if I'm wrong, but I vaguely > remember that there is a JEP for java to auto compile java files and > run them), but I find that to be optimistic, since if somebody wants > something like that, the small extra boilerplate won't stop them. So, > basically this JEP provides a minimal simplification of writing an > entry point, and entry points are rare (compared to the rest of the > code). This is merely my opinion, but writing out the small boiler > plate for the entry point didn't bother me at all. Thus, I would say > that this JEP provides little benefit to anyone but beginners. Or > rather, I would throw back the question: What could I gain from using > this? What value do I get from this as a non-beginner? (not trying to > mock you, I'm honestly curious what am I missing here) I did see you > mention on another list that abstract classes could have a main > method, but if we want such a thing, that need not be tied to this > JEP, and we shouldn't prevent future possibilities by introducing this > JEP (such as easier writing of utility classes). > > Brian Goetz ezt ?rta (id?pont: 2023. ?pr. > 10., H, 20:17): >> >>> While I like the idea of making the language more beginner friendly, I >>> don't think the proposal in this form is the best way, because it does >>> exactly what it claims to want to avoid: It introduces a separate >>> beginner's dialect. That is, a form of writing code only to be used by >>> beginners and never again. >> >> Since you provide no argument or examples to support your assertion, it is pretty hard to tell where you?ve gone off the trail here. >> >> But your claim that the features here are useful only to beginners and never again is simply incorrect. If you want to unpack your claim, perhaps we can clarify the disconnect. >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From nathan.h.walker at gmail.com Tue Apr 11 01:53:44 2023 From: nathan.h.walker at gmail.com (Nathan Walker) Date: Mon, 10 Apr 2023 21:53:44 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes, Preview Message-ID: After reading the JEP, I am a bit unclear on how to launch a pre-compiled anonymous class, or if that will even be permitted. The JEP gives this example: java HelloWorld.java Makes complete sense to me. But then it goes on to say: In this case the compiler chooses HelloWorld for the class name as an > implementation detail, but that name still cannot be used directly in Java > source code. It also says in an earlier part An anonymous main class cannot be referenced by name So, if I package up my anonymous class for deployment in a jar, can I launch it with "java -cp myjar.jar HelloWorld"? Or is the HelloWorld name an implementation detail that can not be relied on here? My first impression on reading this JEP was that it was limited to source file programs like in the example (which would be a shame). But with further reflection on this earlier statement that didn't seem correct: An anonymous main class resides in the unnamed package, and the unnamed > package resides in the unnamed module. While there can be only one unnamed > package (barring multiple class loaders) and only one unnamed module, > ***there can be multiple anonymous main classes in the unnamed module***. > Every anonymous main class contains a main method and so represents a > program, thus multiple such classes in the unnamed package represent > multiple programs. I am not sure what use multiple anonymous main classes would have if we can't target which one we want to launch, so I feel like I am missing something here. Thanks for your time, Nathan -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Apr 11 03:10:24 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 10 Apr 2023 23:10:24 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) Message-ID: Hello Mark, Thank you for posting this! If it wasn't the middle of the night, I would be clapping and cheering. This is exactly what I have been hoping for for over 10 years. I see so many uses for these changes and my ability to write better and more organized code is going to shoot up a LOT thanks to this. Thank you all for making it happen! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Tue Apr 11 04:56:25 2023 From: redio.development at gmail.com (Red IO) Date: Tue, 11 Apr 2023 06:56:25 +0200 Subject: JEP 445 Message-ID: I was reading the discussions on "anonymous class declarations" which in my understanding simply means that the file scope is treated as class scope of a class named after the file. It was also mentioned that those classes would not be accessible for other source files to reference. My question is then why should we treat them so special? The class name is already strongly coupled to the file name except for package private classes in a file. Why not treat them as regular classes with fields and instances and all? The only reason I see is to make static variables and the static of main implicit which is a weak argument for treating these classes so special in my opinion. If we treat them as regular classes it would even be possible to have generics or inheritance with an optional partial class header syntax like for example: //file HelloWorld.java import xyz; class implements AutoClosable; private T t; @Override public void close() {} static int myGlobal = 5; static void main() { myGlobal = 51; var hello = new HelloWorld(); hello.t = myGlobal; hello.close(); } I just think by restricting the implicit class scope to only be useful for entry points it makes it a weak feature while restricting many other use cases. Great regards RedIODev -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Apr 11 08:23:25 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 11 Apr 2023 08:23:25 +0000 Subject: JEP 445 In-Reply-To: References: Message-ID: <30F57710-7C7F-40B0-B851-C9BEE6D5D451@oracle.com> > On 11 Apr 2023, at 05:56, Red IO wrote: > > I was reading the discussions on "anonymous class declarations" which in my understanding simply means that the file scope is treated as class scope of a class named after the file. It was also mentioned that those classes would not be accessible for other source files to reference. As far as Java code is concerned, the class is not named after the file; it is anonymous. > > My question is then why should we treat them so special? > The class name is already strongly coupled to the file name except for package private classes in a file. > > Why not treat them as regular classes with fields and instances and all? They can have fields and methods and member classes and even an instance ? just like anonymous classes today can. But just like anonymous classes, they can only be instantiated ?externally?. > > The only reason I see is to make static variables and the static of main implicit which is a weak argument for treating these classes so special in my opinion. There are no implicit statics here. Nothing is changed from the syntax and meaning of anonymous classes. > > If we treat them as regular classes it would even be possible to have generics or inheritance with an optional partial class header syntax like for example: Nothing is restricted. If you want generics and inheritance, just add a class declaration. The meaning of everything will be preserved. ? Ron From ron.pressler at oracle.com Tue Apr 11 09:35:45 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 11 Apr 2023 09:35:45 +0000 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes, Preview In-Reply-To: References: Message-ID: <7919B6B5-2867-4EC8-8BFE-3080B683137E@oracle.com> > On 11 Apr 2023, at 02:53, Nathan Walker wrote: > > > So, if I package up my anonymous class for deployment in a jar, can I launch it with "java -cp myjar.jar HelloWorld"? Or is the HelloWorld name an implementation detail that can not be relied on here? My first impression on reading this JEP was that it was limited to source file programs like in the example (which would be a shame). You can launch it with `java -cp myjar.jar HelloWorld`. From the perspective of *Java code* the class is anonymous, so you cannot refer to it by name from *Java code*, but the launcher can refer to it by the name of its file. ? Ron From ron.pressler at oracle.com Tue Apr 11 09:46:40 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 11 Apr 2023 09:46:40 +0000 Subject: [External] : Re: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: > On 10 Apr 2023, at 19:25, Glavo wrote: > > I like some parts of this proposal, but I disagree with it. I think this proposal is too narrow and limits its usefulness. > > Is the implicit public and static modifiers strange? The interface has worked this way for many years, why is it not acceptable here? The alternatives section explains why treating members as static is not acceptable. Making them implicitly public makes little sense, as the class is anonymous and so can?t be referenced from other classes anyway. Since it has no declaration it also cannot it implement interfaces. Note that the body of the anonymous class accepts anything that can be in the body of an anonymous class today, and *its meaning is the same*. There are no new interpretations of syntax here. Interpreting members as implicitly static would be a new interpretation, and it would make it harder to evolve into a named class, where the rules would be different. Also, note that the flexibility in the main methods is orthogonal to anonymous main classes. The new main methods work even in named classes. > I think this could be a step towards package-level declarations, whereas the current proposal limits the direction it can go. The current proposal does not limit that future direction. The reason is that anonymous main classes are singletons, and some future package-level declarations would also be singletons, and when it comes to singletons, there is little difference between static and instance members. ? Ron From holo3146 at gmail.com Tue Apr 11 20:03:26 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Tue, 11 Apr 2023 23:03:26 +0300 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: >2. It would also make writing utility classes easier, thus benefiting more people This is a non-goal, the anonymous main class can only be in the unnamed package of the unnamed module, meaning it is very restricted in use, you can also not reference any method (static or not) of the class from the outside, so it is pretty much impossible to use this feature for utility classes from design. >3. It would allow (assuming the implied class would be visible to others) students to first "modularize" their application into separate logical units before going to classes. Again, the class is not visible by design. I also don't understand why it will help modularize, the modularization order should be: "all in main" -> "methods" -> "classes (and friends)" -> "files" If you swap classes and files you ran into a risk of people stop using classes all-together, while stop using files is very unlikely (furthermore, it is ordered by size) >4. It would not require touching the "main" finding logic. That could be considered separately in another JEP. But the static solution would require new semantics and an unnamed class, which are arguably as big of a language feature On Tue, Apr 11, 2023, 19:31 Attila Kelemen wrote: > Thanks. I don't want to make an estimate on how much boilerplate > removal helps this to spread for shebang given that I didn't even know > it's already supported (and as far as I can see it is supported for > quite a long time now). So, I'll just assume that it will (there is > really no good reason to assume otherwise anyway). > > What remains for me is that I don't see why not the "Interpret code > units as static members" alternative was chosen. That section of the > JEP provides two reasons: > > 1. It would make "static" implicit which is not the case in normal > classes. However, default modifiers depending on the context is > already a thing: Enum constructors are private by default, interface > members are public by default, while interface fields are implicitly > static. I don't see why this particular case would be a heresy. > > 2. Copy pasting the code when "upgrading" to classes would change the > code. However, this is only a noticeable issue, if some methods are > explicitly marked as "static", which is unlikely (might happen due to > copy pasting from external sources), and even if that were to happen, > the worst case scenario is a compilation error which is easy to fix, > and might even have educational value to the student. Or, if fields > were marked explicitly static, in which case it will matter if more > than one instance of that class is created (which I think is still > unlikely at this point). > > However, there are also benefits to the "static" approach (which seems > to outweigh the cons for me) to basically make it a utility class: > > 1. If we are talking about a non-beginner programmer, but someone new > to Java, I would argue that it is less surprising to consider "top > level" functions to be static. Of course, if the implied class is not > visible to others, then it doesn't make much difference (same way as > with the con 2). > > 2. It would also make writing utility classes easier, thus benefiting > more people. > > 3. It would allow (assuming the implied class would be visible to > others) students to first "modularize" their application into separate > logical units before going to classes. At least in my mind, the > logical progress of learning would be (in order): functions + basic > types, loops and such, arrays, records, organizing code into logical > units, classes: make that logical unit non-static, ... > > 4. It would not require touching the "main" finding logic. That could > be considered separately in another JEP. > > So, for me it seems that the "static" approach would have more > utility. Pun totally intended. If you want, I can even provide a much > more detailed description of how I imagine such a "static" solution > would work. > > Attila > > Brian Goetz ezt ?rta (id?pont: 2023. ?pr. > 11., K, 1:24): > > > > As you allude to, there is already the ?Single File Source Launcher?, > which let?s you run a Java program directly from the command line without > compilation (`java Foo.java`), which has been around for a while, and > additionally does support ?shebang? execution. (There are plans to extend > this further.). This was aimed at script-like programs, which, while not > written by beginners, also do not require the ceremony of the class > declaration. So the design center here is not ?beginning programmers?, but > ?simple programs?, but of course there is a lot of intersection between the > two ? beginning programmers write simple programs. > > > > There is also a chicken-and-egg problem here; one of the reasons people > don?t write script-like programs in Java so often is because of the > declaration and building overhead. But that?s an accidental consequence, > and we can remove these impediments. So students may be the most obvious > group of beneficiaries, but they are not the only ones. > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From hunor.szegi at gmail.com Tue Apr 11 20:29:33 2023 From: hunor.szegi at gmail.com (Hunor Szegi) Date: Tue, 11 Apr 2023 21:29:33 +0100 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) Message-ID: Hi All, Thank you for this new proposal, I like the idea of JEP 445 with the flexible main methods. My only concern is the implicit "static" keyword. Which means the initially learned main method format should be modified later, so this doesn't really support the gradual learning curve. It is even a little bit against the goal "Do not introduce a separate beginner's dialect of Java." I would consider a bigger step, supporting a main() method without the void return type and modifiers (Similarly to the constructor declaration, where the missing return type makes it a different kind of thing. And the name is also fixed there. But in this case we don't need the public and static modifier.) It would not be specific for the anonymous classes. (So it isn't a separate beginner's dialect.) I know it would create a new language element. But all the problems with another method named "main" will be gone. (No need for the "selecting the main method" logic.) Thanks, Hunor -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Tue Apr 11 20:37:44 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Tue, 11 Apr 2023 22:37:44 +0200 (CEST) Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: Message-ID: <1519233318.34457503.1681245464294.JavaMail.zimbra@univ-eiffel.fr> > From: "Hunor Szegi" > To: "amber-dev" > Sent: Tuesday, April 11, 2023 10:29:33 PM > Subject: Re: New candidate JEP: 445: Flexible Main Methods and Anonymous Main > Classes (Preview) > Hi All, > Thank you for this new proposal, I like the idea of JEP 445 with the flexible > main methods. My only concern is the implicit "static" keyword. Which means the > initially learned main method format should be modified later, so this doesn't > really support the gradual learning curve. It is even a little bit against the > goal "Do not introduce a separate beginner's dialect of Java." There is no implicit static. It works the other way around, the launcher first checks for a static main and if none is found, it checks for a non static main and a default constructor. So I repeat, there is no implicit static. [...] > Thanks, > Hunor regards, R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From hunor.szegi at gmail.com Tue Apr 11 21:26:29 2023 From: hunor.szegi at gmail.com (Hunor Szegi) Date: Tue, 11 Apr 2023 22:26:29 +0100 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: <1519233318.34457503.1681245464294.JavaMail.zimbra@univ-eiffel.fr> References: <1519233318.34457503.1681245464294.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Ok, I phrased it incorrectly. I see that technically it isn't static. My concern is that the "main" method is usually static (until now it is always that), but now you will be able to create a main method without the "static" keyword. So for a beginner, without understanding fully the difference between static and non-static (which isn't easy!) it seems that the main became implicitly static. At least it behaves very similarly, and it will work with or without the static keyword. So it can lead to confusion. The class instantiation is hidden, so syntactically it seems the main method became static automatically. That's why I was thinking of a drastically new entry point declaration, which can't be mixed up with the old static method. To avoid that order, the main method is looked up. In theory it could be named differently. Instead of "main" it could be called "start" for example. But I know we should be careful using new words. So I thought that using simply "main()" would be completely different to the current "public static void main()" solution. "If we have the former, the latter should be disallowed." So in practice it could be used as a simpler entry point. (With or without args.) Hunor On Tue, 11 Apr 2023 at 21:37, Remi Forax wrote: > > > ------------------------------ > > *From: *"Hunor Szegi" > *To: *"amber-dev" > *Sent: *Tuesday, April 11, 2023 10:29:33 PM > *Subject: *Re: New candidate JEP: 445: Flexible Main Methods and > Anonymous Main Classes (Preview) > > Hi All, > > Thank you for this new proposal, I like the idea of JEP 445 with the > flexible main methods. My only concern is the implicit "static" keyword. > Which means the initially learned main method format should be modified > later, so this doesn't really support the gradual learning curve. It is > even a little bit against the goal "Do not introduce a separate beginner's > dialect of Java." > > > There is no implicit static. > It works the other way around, the launcher first checks for a static main > and if none is found, it checks for a non static main and a default > constructor. > > So I repeat, there is no implicit static. > > [...] > > > Thanks, > Hunor > > > regards, > R?mi > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Tue Apr 11 21:49:18 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Tue, 11 Apr 2023 21:49:18 +0000 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <1519233318.34457503.1681245464294.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <92CA88E1-375E-490C-8D44-EE0979C66594@oracle.com> On 11 Apr 2023, at 22:26, Hunor Szegi > wrote: Ok, I phrased it incorrectly. I see that technically it isn't static. My concern is that the "main" method is usually static (until now it is always that), but now you will be able to create a main method without the "static" keyword. So for a beginner, without understanding fully the difference between static and non-static (which isn't easy!) it seems that the main became implicitly static. At least it behaves very similarly, and it will work with or without the static keyword. So it can lead to confusion. The class instantiation is hidden, so syntactically it seems the main method became static automatically. 1. Flexible main methods don?t only work in anonymous main classes but all classes, so there?s never a need to change anything. 2. Anyone who knows what main ?usually? used to be can continue using that flavour if they prefer. 3. The signature of main methods is, and has always been, rather arbitrary. There?s nothing fundamental that says it should be static or an instance method. Instance main methods don?t work ?as if they were static?; rather, they work like instance main methods should work, and would have worked if they had existed in Java since day one. 4. Indeed, a class should have one entry point, and linters/IDEs will warn if there are multiple ones. A corpus search revealed that the portion of existing classes with more than one candidate main method is negligible, so we don?t think it would be a problem in practice. The ordering is needed for backward compatibility. Having said all that, if, after trying the feature you find that you run into problems, please report them to this mailing list. One thing we could consider changing is to emit an error on multiple candidates as long as none of them is the current signature. ? Ron -------------- next part -------------- An HTML attachment was scrubbed... URL: From roberts14 at cablelink.at Tue Apr 11 12:43:00 2023 From: roberts14 at cablelink.at (Robert) Date: Tue, 11 Apr 2023 14:43:00 +0200 Subject: JEP 441: Pattern Matching for switch (suggestion re: handling sealed type selector expressions) In-Reply-To: References: <2F768D17-88B7-4D71-B31B-AD8C884A1370@cablelink.at> Message-ID: <528476F5-3254-4A69-8FF5-DBDCACD41C5E@cablelink.at> Thanks Brian and Holo TSW (in other post) for your thoughtful responses. I stand enlightened. My takeaway is that sealed types are not only not special, but that they have common and important use-cases (e.g. parser and json mapping) that indeed depend on the kind of scalability that catchall labels provide, to avoid everyone?s (rightly) loathed boilerplate and verbosity. Probably Holo?s linter solution suffices. > On Apr 9, 2023, at 20:18, Brian Goetz wrote: > > I understand why you were motivated to make this suggestion. But, I think the restriction you propose is overly restrictive. > > First let me amplify on your motivation, as this was an important motivation in the design of this feature: a switch that is exhaustive without a catch-all case is better than a switch that is exhaustive with one. This is not because you don?t have to write the catch all case when you cover all the cases, though that is a nice side benefit. It is because you get _better type checking_. The compiler verifies your intent of exhaustiveness, and triggers an error if this is undermined by later additions to an enum or sealed class. A default sweeps the sin under the rug. > > So, you say, if better type checking is better, why not force everyone to get that better type checking by being exhaustive the honest way? This is where things start to go off; there are too many legitimate use cases that this excludes. > > First, consider the case where you have a large set of choices (such as tokens in a parser), but you only want to act on a few of them. Switch is a good tool for this: > > status = switch (nextToken) { > case OPEN_PAREN -> ? > case IDENTIFIER -> ? > default -> throw new ParseException(?Expecting identifier or parentheses?); > } > > This code is fine! It is clear what is going on ? we?re expecting one of two tokens, and anything else is an error. Is the user helped by being required to list the other 72 tokens explicitly instead of a catch-all? Is the code made more readable? > > Another example is the common pattern of a switch with two clauses, which is often more appropriate than an if: > > boolean shouldStop = switch (color) { > case RED -> true; > default -> false; > } > > Yes, this could be a ternary, but this pattern is pretty useful and pretty clear, and ternaries don?t scale. Having to list out YELLOW and GREEN in the second case, even though there are only two, would not be an improvement. > > I think what tripped you up was you extrapolated from a set of examples where you really were handling all, or nearly all, of the cases. But switches are useful when you only want to handle one or two cases specially too. > > >> On Apr 8, 2023, at 10:09 AM, Robert > wrote: >> >> Hi all, newbie here. >> >> First off ? loving Amber features. >> >> Now, a *very narrow* observation about how JDK 20 handles sealed type selector expressions. >> (Code snippet below.) >> >> If a sealed type S later grows its list of permitted subtypes at some point, >> how should the compiler handle switch blocks with S as selector? >> >> In the absence of a catchall label (i.e. ?case default -> ...? or ?case S s -> ...?) , >> the compiler already correctly issues an error that coverage is incomplete. >> >> But if the block ended with a catchall label, the compiler is silent; the best that >> can be hoped-for is that the coder throws a defensive exception in the catchall label >> (and hope that it is triggered in testing). >> >> Suggestion: >> >> Compiler should *disallows* catchall statements in switch blocks that use a sealed type >> in the switch selector expression. Note that this special case does not affect all the >> other non-sealed-type selectors (e.g. Object o), and appears (to me) to strengthen the >> safety-value of sealed types in this particular scenario (similar to enums). >> >> All in Java?s spirit of ?least surprise?. >> >> Cheers, >> Robert >> >> >> PS. reported behavior confirmed using jshell in JDK 20: >> >> jshell --enable-preview >> | Welcome to JShell -- Version 20 >> | For an introduction type: /help intro >> >> on the following: >> >> sealed interface S permits S.X, S.Y, S.Z, S.NEWBIE { >> >> final class X implements S {}; >> >> final class Y implements S {}; >> >> final class Z implements S {}; >> >> final class NEWBIE implements S {}; >> >> public static void main(String [] sa) { >> >> S something = new NEWBIE(); >> System.out.println( >> switch (something) { >> case null -> 0; >> case X x -> 1; >> case Y y -> 2; >> case Z z -> 3; >> case S s -> -1; >> // case default -> -1; >> }); >> }; >> } >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Tue Apr 11 16:31:19 2023 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 11 Apr 2023 18:31:19 +0200 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: Thanks. I don't want to make an estimate on how much boilerplate removal helps this to spread for shebang given that I didn't even know it's already supported (and as far as I can see it is supported for quite a long time now). So, I'll just assume that it will (there is really no good reason to assume otherwise anyway). What remains for me is that I don't see why not the "Interpret code units as static members" alternative was chosen. That section of the JEP provides two reasons: 1. It would make "static" implicit which is not the case in normal classes. However, default modifiers depending on the context is already a thing: Enum constructors are private by default, interface members are public by default, while interface fields are implicitly static. I don't see why this particular case would be a heresy. 2. Copy pasting the code when "upgrading" to classes would change the code. However, this is only a noticeable issue, if some methods are explicitly marked as "static", which is unlikely (might happen due to copy pasting from external sources), and even if that were to happen, the worst case scenario is a compilation error which is easy to fix, and might even have educational value to the student. Or, if fields were marked explicitly static, in which case it will matter if more than one instance of that class is created (which I think is still unlikely at this point). However, there are also benefits to the "static" approach (which seems to outweigh the cons for me) to basically make it a utility class: 1. If we are talking about a non-beginner programmer, but someone new to Java, I would argue that it is less surprising to consider "top level" functions to be static. Of course, if the implied class is not visible to others, then it doesn't make much difference (same way as with the con 2). 2. It would also make writing utility classes easier, thus benefiting more people. 3. It would allow (assuming the implied class would be visible to others) students to first "modularize" their application into separate logical units before going to classes. At least in my mind, the logical progress of learning would be (in order): functions + basic types, loops and such, arrays, records, organizing code into logical units, classes: make that logical unit non-static, ... 4. It would not require touching the "main" finding logic. That could be considered separately in another JEP. So, for me it seems that the "static" approach would have more utility. Pun totally intended. If you want, I can even provide a much more detailed description of how I imagine such a "static" solution would work. Attila Brian Goetz ezt ?rta (id?pont: 2023. ?pr. 11., K, 1:24): > > As you allude to, there is already the ?Single File Source Launcher?, which let?s you run a Java program directly from the command line without compilation (`java Foo.java`), which has been around for a while, and additionally does support ?shebang? execution. (There are plans to extend this further.). This was aimed at script-like programs, which, while not written by beginners, also do not require the ceremony of the class declaration. So the design center here is not ?beginning programmers?, but ?simple programs?, but of course there is a lot of intersection between the two ? beginning programmers write simple programs. > > There is also a chicken-and-egg problem here; one of the reasons people don?t write script-like programs in Java so often is because of the declaration and building overhead. But that?s an accidental consequence, and we can remove these impediments. So students may be the most obvious group of beneficiaries, but they are not the only ones. > From attila.kelemen85 at gmail.com Tue Apr 11 20:47:44 2023 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Tue, 11 Apr 2023 22:47:44 +0200 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <20230410173701.2A6B05D1AD9@eggemoggin.niobe.net> Message-ID: > >2. It would also make writing utility classes easier, thus benefiting > more people > > This is a non-goal, the anonymous main class can only be in the unnamed package of the unnamed module, meaning it is very restricted in use, you can also not reference any method (static or not) of the class from the outside, so it is pretty much impossible to use this feature for utility classes from design. The fact that the JEP does not declare this as a goal doesn't mean that it shouldn't be done, and the JEP specifically takes away this possibility forever. That is, another way which would provide the same values (and I would argue more) cannot be just swept away by saying that the original idea didn't declare this as a goal. Even more specifically, I'm arguing for replacing this JEP with something else, and I can't argue it in another context, because this JEP would prevent it from ever happening. > > >3. It would allow (assuming the implied class would be visible to > others) students to first "modularize" their application into separate > logical units before going to classes. > > Again, the class is not visible by design. I also don't understand why it will help modularize, the modularization order should be: "all in main" -> "methods" -> "classes (and friends)" -> "files" > Because as is now everything has to be in a single file. Organizing things into multiple files is a form of modularization, even if on a very small level, but students should start small. > If you swap classes and files you ran into a risk of people stop using classes all-together, while stop using files is very unlikely (furthermore, it is ordered by size) > > >4. It would not require touching the "main" finding logic. That could > be considered separately in another JEP. > > But the static solution would require new semantics and an unnamed class, which are arguably as big of a language feature What are the default modifiers is already context dependent. It wouldn't be such a new concept, and the current proposal is already a big language feature of which this would be just one of its properties. Compared to imagining it inside an anonymous class it is less in my opinion. From abimpoudis at openjdk.org Thu Apr 13 07:09:26 2023 From: abimpoudis at openjdk.org (Aggelos Biboudis) Date: Thu, 13 Apr 2023 07:09:26 GMT Subject: [patterns-instanceof-primitive] RFR: 8303374: Compiler Implementation for Primitive types in patterns, instanceof, and switch [v3] In-Reply-To: References: Message-ID: <7zAcUoDH3lrqZI1GTSXD_vdUvF76nQ2xb82yfUvT7cE=.ad44ac10-d9d0-4bfb-a8de-e308115126d3@github.com> On Sun, 12 Mar 2023 08:43:38 GMT, Aggelos Biboudis wrote: >> Prototype implementation for primitive types in patterns, instanceof, and switch. >> >> draft JEP: https://openjdk.org/jeps/8288476 >> >> draft spec: https://cr.openjdk.org/~abimpoudis/instanceof/latest/ > > Aggelos Biboudis has updated the pull request incrementally with one additional commit since the last revision: > > Improve ExactnessMethods > > Co-authored-by: Raffaello Giulietti > Co-authored-by: Angelos Bimpoudis Keep it open ------------- PR Comment: https://git.openjdk.org/amber/pull/91#issuecomment-1506458352 From andrew.evers at gmail.com Thu Apr 13 14:30:41 2023 From: andrew.evers at gmail.com (Andrew Evers) Date: Thu, 13 Apr 2023 10:30:41 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) Message-ID: Hi, I really like the idea of reducing the amount of boilerplate required to get to a simple Java program. Two comments: 1. Would it be at all possible to consider a main that returns int? Operating systems that don't support it are free to disregard it, and it feels better than System.exit(). It also allows skipping the idea of void, which is somewhat of a holdover from C. 2. I have an idea for a slightly different approach to the problem that may solve a few of the problems being proposed, and allows for imports, which would make it more useful for scripting use cases. Here are three examples showing the syntax. Example 1: { System.out.println("Hello, World"); } Example 2: import java.nio.*; { Path filePath = Path.of("file.txt"); String fileContent = new String(Files.readAllBytes(filePath)); System.out.println("Content is " + fileContent); } Example 3: import java.nio.*; { Path filePath = Path.of("file.txt"); String fileContent = new String(Files.readAllBytes(filePath)); greet(fileContent); } void greet(String s) { System.out.println("Content is " + s); } The rules are simple: 1. If the first non-whitespace is a {, then treat it as the block defining the method public static void main(String [] args). 2. If the first non-whitespace is "import" then capture the imports. 3. If there is non-whitespace after the closing } in the main block, include it before the class closing brace. That is: // import statements if any class Main { public static void main(String [] args) { // Content including { from source } // end of content, including } // Content after closing } in source, if any. } There are some minor fixups required on line numbers after the import statements, these can be overcome by either ensuring that the additional code takes up zero lines, or by fixing up the output of the compiler. Regards, Andrew. -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Thu Apr 13 16:55:45 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Thu, 13 Apr 2023 19:55:45 +0300 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: Message-ID: I can't comment on the first point, but the second point won't work. The syntax you suggested creates at best an order of declaration dependency between the entry point and the instance initialized block, and at worst an ambiguity to the compiler. Also, I believe that in the current solution it is possible to import in an unnamed main class. On Thu, Apr 13, 2023, 18:49 Andrew Evers wrote: > Hi, > > I really like the idea of reducing the amount of boilerplate required to > get to a simple Java program. > > Two comments: > 1. Would it be at all possible to consider a main that returns int? > Operating systems that don't support it are free to disregard it, and it > feels better than System.exit(). It also allows skipping the idea of void, > which is somewhat of a holdover from C. > 2. I have an idea for a slightly different approach to the problem that > may solve a few of the problems being proposed, and allows for imports, > which would make it more useful for scripting use cases. > > Here are three examples showing the syntax. > > Example 1: > { > System.out.println("Hello, World"); > } > > Example 2: > import java.nio.*; > > { > Path filePath = Path.of("file.txt"); > String fileContent = new String(Files.readAllBytes(filePath)); > System.out.println("Content is " + fileContent); > } > > Example 3: > import java.nio.*; > > { > Path filePath = Path.of("file.txt"); > String fileContent = new String(Files.readAllBytes(filePath)); > greet(fileContent); > } > > void greet(String s) > { > System.out.println("Content is " + s); > } > > The rules are simple: > 1. If the first non-whitespace is a {, then treat it as the block defining > the method public static void main(String [] args). > 2. If the first non-whitespace is "import" then capture the imports. > 3. If there is non-whitespace after the closing } in the main block, > include it before the class closing brace. > > That is: > > // import statements if any > class Main > { > public static void main(String [] args) > { // Content including { from source > } // end of content, including } > > // Content after closing } in source, if any. > } > > There are some minor fixups required on line numbers after the import > statements, these can be overcome by either ensuring that the additional > code takes up zero lines, or by fixing up the output of the compiler. > > Regards, > > Andrew. > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.evers at gmail.com Thu Apr 13 17:12:47 2023 From: andrew.evers at gmail.com (Andrew Evers) Date: Thu, 13 Apr 2023 13:12:47 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: Message-ID: There isn't an ambiguity as the simplified syntax I'm proposing doesn't allow for an instance initialization block. For the instance initialization block to be available, you must be in a class declaration, at which point you're in the full syntax. Parsing would be as follows, if the first token is: * package - parse and work as a normal java class * import - parse import statements until you reach either '{' or a token that starting with a letter - if a letter, then treat as a normal java class - if { use this syntax. When I have implemented this in the past, I've written it as a frontend and generated an underlying Java source file to pass to the standard compiler (the template using class Main is an example), then done line number fixups on the errors. On Thu, Apr 13, 2023 at 12:55?PM Holo The Sage Wolf wrote: > I can't comment on the first point, but the second point won't work. > The syntax you suggested creates at best an order of declaration > dependency between the entry point and the instance initialized block, and > at worst an ambiguity to the compiler. > > Also, I believe that in the current solution it is possible to import in > an unnamed main class. > > On Thu, Apr 13, 2023, 18:49 Andrew Evers wrote: > >> Hi, >> >> I really like the idea of reducing the amount of boilerplate required to >> get to a simple Java program. >> >> Two comments: >> 1. Would it be at all possible to consider a main that returns int? >> Operating systems that don't support it are free to disregard it, and it >> feels better than System.exit(). It also allows skipping the idea of void, >> which is somewhat of a holdover from C. >> 2. I have an idea for a slightly different approach to the problem that >> may solve a few of the problems being proposed, and allows for imports, >> which would make it more useful for scripting use cases. >> >> Here are three examples showing the syntax. >> >> Example 1: >> { >> System.out.println("Hello, World"); >> } >> >> Example 2: >> import java.nio.*; >> >> { >> Path filePath = Path.of("file.txt"); >> String fileContent = new String(Files.readAllBytes(filePath)); >> System.out.println("Content is " + fileContent); >> } >> >> Example 3: >> import java.nio.*; >> >> { >> Path filePath = Path.of("file.txt"); >> String fileContent = new String(Files.readAllBytes(filePath)); >> greet(fileContent); >> } >> >> void greet(String s) >> { >> System.out.println("Content is " + s); >> } >> >> The rules are simple: >> 1. If the first non-whitespace is a {, then treat it as the block >> defining the method public static void main(String [] args). >> 2. If the first non-whitespace is "import" then capture the imports. >> 3. If there is non-whitespace after the closing } in the main block, >> include it before the class closing brace. >> >> That is: >> >> // import statements if any >> class Main >> { >> public static void main(String [] args) >> { // Content including { from source >> } // end of content, including } >> >> // Content after closing } in source, if any. >> } >> >> There are some minor fixups required on line numbers after the import >> statements, these can be overcome by either ensuring that the additional >> code takes up zero lines, or by fixing up the output of the compiler. >> >> Regards, >> >> Andrew. >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Apr 17 14:14:23 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 17 Apr 2023 10:14:23 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: Message-ID: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> > 1. Would it be at all possible to consider a main that returns int? > Operating systems that don't support it are free to disregard it, and > it feels better than System.exit(). It also?allows skipping the idea > of void, which is somewhat of a holdover from C. This is something we've left room to consider in the future, but feels like "scope creep" at the present time. > 2. I have an idea for a slightly different approach to the problem > that may solve a few of the problems being proposed, and allows for > imports, which would make it more useful for scripting use cases. AFAICS, the spirit of what you're suggesting is to say that we can treat an instance initializer as a "main" method. I would put this in the category of "clever" ideas, where you're repurposing an existing language construct (instance initializers) to avoid typing the characters "void main()".?? Unfortunately, this fails the "on ramp leads gracefully into the highway" test; when the class gets complicated enough to need to be a named class, the instance initializer sticks out like a sore thumb, because the author didn't want an instance initializer, they wanted a main method. From andrew.evers at gmail.com Mon Apr 17 14:24:59 2023 From: andrew.evers at gmail.com (Andrew Evers) Date: Mon, 17 Apr 2023 10:24:59 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> References: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> Message-ID: Hi, 1. Understood. 2. I'm not proposing instance initializers, but this has caught two people now. I'm proposing that absent a class declaration, the first block _is_ the main method. You could even argue for the removal of the braces, but that makes adding methods difficult. This is similar to the shell, python, PHP, and PL/SQL anonymous blocks. On Mon, Apr 17, 2023 at 10:14?AM Brian Goetz wrote: > > > 1. Would it be at all possible to consider a main that returns int? > > Operating systems that don't support it are free to disregard it, and > > it feels better than System.exit(). It also allows skipping the idea > > of void, which is somewhat of a holdover from C. > > This is something we've left room to consider in the future, but feels > like "scope creep" at the present time. > > > 2. I have an idea for a slightly different approach to the problem > > that may solve a few of the problems being proposed, and allows for > > imports, which would make it more useful for scripting use cases. > > AFAICS, the spirit of what you're suggesting is to say that we can treat > an instance initializer as a "main" method. > > I would put this in the category of "clever" ideas, where you're > repurposing an existing language construct (instance initializers) to > avoid typing the characters "void main()". Unfortunately, this fails > the "on ramp leads gracefully into the highway" test; when the class > gets complicated enough to need to be a named class, the instance > initializer sticks out like a sore thumb, because the author didn't want > an instance initializer, they wanted a main method. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Apr 17 14:50:33 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 17 Apr 2023 10:50:33 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> Message-ID: <6be200cf-f104-dcfa-58b7-7144a29268a5@oracle.com> If the blocks in your proposal are not instance initializers, this unfortunately makes it worse: ?- Even ignoring any possible syntactic ambiguity with instance initializers, now there are two identical-looking constructs in the language that mean subtly different things based on context. ?- The "main blocks" are even more of an on-ramp-to-nowhere, since they would be illegal (or worse, mean something else!) in a full class.? Even without the conflict, this is creating a dead-end dialect for the sake of avoiding a few keystrokes. Ultimately I think this proposal is motivated more by minimizing character count than by reducing the number of concepts that simple programs must use, and as a result, ends up creating net new complexity. On 4/17/2023 10:24 AM, Andrew Evers wrote: > Hi, > > 1. Understood. > > 2. I'm not proposing instance initializers, but this has caught two > people now. > > I'm proposing that absent a class declaration, the first block _is_ > the main method. You could even argue for the removal of the braces, > but that makes adding methods difficult. This is similar to the shell, > python, PHP, and PL/SQL anonymous blocks. > > On Mon, Apr 17, 2023 at 10:14?AM Brian Goetz > wrote: > > > > 1. Would it be at all possible to consider a main that returns int? > > Operating systems that don't support it are free to disregard > it, and > > it feels better than System.exit(). It also?allows skipping the > idea > > of void, which is somewhat of a holdover from C. > > This is something we've left room to consider in the future, but > feels > like "scope creep" at the present time. > > > 2. I have an idea for a slightly different approach to the problem > > that may solve a few of the problems being proposed, and allows for > > imports, which would make it more useful for scripting use cases. > > AFAICS, the spirit of what you're suggesting is to say that we can > treat > an instance initializer as a "main" method. > > I would put this in the category of "clever" ideas, where you're > repurposing an existing language construct (instance initializers) to > avoid typing the characters "void main()".?? Unfortunately, this > fails > the "on ramp leads gracefully into the highway" test; when the class > gets complicated enough to need to be a named class, the instance > initializer sticks out like a sore thumb, because the author > didn't want > an instance initializer, they wanted a main method. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.evers at gmail.com Mon Apr 17 15:34:59 2023 From: andrew.evers at gmail.com (Andrew Evers) Date: Mon, 17 Apr 2023 11:34:59 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: <6be200cf-f104-dcfa-58b7-7144a29268a5@oracle.com> References: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> <6be200cf-f104-dcfa-58b7-7144a29268a5@oracle.com> Message-ID: > If the blocks in your proposal are not instance initializers, > this unfortunately makes it worse: > > - Even ignoring any possible syntactic ambiguity with instance > initializers, now there are two identical-looking constructs in > the language that mean subtly different things based on context. The code is a Block, allowed in a lot of places. Context is exactly the point. In the existing proposal both the developer and some parts of Java need to be aware (within a few tokens of starting a compilation unit) of two distinct contexts: a normal class and an anonymous main class. > - The "main blocks" are even more of an on-ramp-to-nowhere, since > they would be illegal (or worse, mean something else!) in a full > class. Even without the conflict, this is creating a dead-end dialect for the sake of avoiding a > few keystrokes. There is a clear pathway from the dialect to a full class, provided in the original email. If there is a goal that this proposal be a valid ClassBodyDeclaration in the existing syntax, that should be clarified. > Ultimately I think this proposal is motivated more by minimizing character count than by reducing > the number of concepts that simple programs must use, and as a result, ends up creating net new > complexity. I would say that it retains the signature of main() exactly as it has been for 20+ years, and allows an additional stage of using imports prior to moving to the full class syntax. It's at most 13 bytes shorter, hardly noticeable. The original motivation of this proposal was when I was using Java as a scripting language. Developers experienced in another language were struggling with the exact concepts discussed here. We gave them a tool that provided them with the option of using the simpler syntax, and how to move to full class syntax when they hit its obvious limitations. We found (over many years, we did this for Java 1.4) that not only was it easier to grasp, but experienced folks would regularly write scripts up to 100 or so lines in the "with imports" version of syntax as it was still more succinct. These scripts would typically be the sort of glue you see tying systems together for process or data integration. The second lesson would apply regardless of the exact syntax, as long as imports are allowed. Without them, the on ramp is very short, since as soon as you need something outside of java.lang, you're typing full package names, and that is tedious (although var at least reduces this). I think it would behoove the group to find a way for import statements to be allowed. On Mon, Apr 17, 2023 at 10:50?AM Brian Goetz wrote: > > If the blocks in your proposal are not instance initializers, this unfortunately makes it worse: > > - Even ignoring any possible syntactic ambiguity with instance initializers, now there are two identical-looking constructs in the language that mean subtly different things based on context. > > - The "main blocks" are even more of an on-ramp-to-nowhere, since they would be illegal (or worse, mean something else!) in a full class. Even without the conflict, this is creating a dead-end dialect for the sake of avoiding a few keystrokes. > > Ultimately I think this proposal is motivated more by minimizing character count than by reducing the number of concepts that simple programs must use, and as a result, ends up creating net new complexity. > > > > On 4/17/2023 10:24 AM, Andrew Evers wrote: > > Hi, > > 1. Understood. > > 2. I'm not proposing instance initializers, but this has caught two people now. > > I'm proposing that absent a class declaration, the first block _is_ the main method. You could even argue for the removal of the braces, but that makes adding methods difficult. This is similar to the shell, python, PHP, and PL/SQL anonymous blocks. > > On Mon, Apr 17, 2023 at 10:14?AM Brian Goetz wrote: >> >> >> > 1. Would it be at all possible to consider a main that returns int? >> > Operating systems that don't support it are free to disregard it, and >> > it feels better than System.exit(). It also allows skipping the idea >> > of void, which is somewhat of a holdover from C. >> >> This is something we've left room to consider in the future, but feels >> like "scope creep" at the present time. >> >> > 2. I have an idea for a slightly different approach to the problem >> > that may solve a few of the problems being proposed, and allows for >> > imports, which would make it more useful for scripting use cases. >> >> AFAICS, the spirit of what you're suggesting is to say that we can treat >> an instance initializer as a "main" method. >> >> I would put this in the category of "clever" ideas, where you're >> repurposing an existing language construct (instance initializers) to >> avoid typing the characters "void main()". Unfortunately, this fails >> the "on ramp leads gracefully into the highway" test; when the class >> gets complicated enough to need to be a named class, the instance >> initializer sticks out like a sore thumb, because the author didn't want >> an instance initializer, they wanted a main method. >> >> On Mon, Apr 17, 2023 at 10:50?AM Brian Goetz wrote: > If the blocks in your proposal are not instance initializers, this > unfortunately makes it worse: > > - Even ignoring any possible syntactic ambiguity with instance > initializers, now there are two identical-looking constructs in the > language that mean subtly different things based on context. > > - The "main blocks" are even more of an on-ramp-to-nowhere, since they > would be illegal (or worse, mean something else!) in a full class. Even > without the conflict, this is creating a dead-end dialect for the sake of > avoiding a few keystrokes. > > Ultimately I think this proposal is motivated more by minimizing character > count than by reducing the number of concepts that simple programs must > use, and as a result, ends up creating net new complexity. > > > > On 4/17/2023 10:24 AM, Andrew Evers wrote: > > Hi, > > 1. Understood. > > 2. I'm not proposing instance initializers, but this has caught two people > now. > > I'm proposing that absent a class declaration, the first block _is_ the > main method. You could even argue for the removal of the braces, but that > makes adding methods difficult. This is similar to the shell, python, PHP, > and PL/SQL anonymous blocks. > > On Mon, Apr 17, 2023 at 10:14?AM Brian Goetz > wrote: > >> >> > 1. Would it be at all possible to consider a main that returns int? >> > Operating systems that don't support it are free to disregard it, and >> > it feels better than System.exit(). It also allows skipping the idea >> > of void, which is somewhat of a holdover from C. >> >> This is something we've left room to consider in the future, but feels >> like "scope creep" at the present time. >> >> > 2. I have an idea for a slightly different approach to the problem >> > that may solve a few of the problems being proposed, and allows for >> > imports, which would make it more useful for scripting use cases. >> >> AFAICS, the spirit of what you're suggesting is to say that we can treat >> an instance initializer as a "main" method. >> >> I would put this in the category of "clever" ideas, where you're >> repurposing an existing language construct (instance initializers) to >> avoid typing the characters "void main()". Unfortunately, this fails >> the "on ramp leads gracefully into the highway" test; when the class >> gets complicated enough to need to be a named class, the instance >> initializer sticks out like a sore thumb, because the author didn't want >> an instance initializer, they wanted a main method. >> > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ron.pressler at oracle.com Mon Apr 17 23:42:10 2023 From: ron.pressler at oracle.com (Ron Pressler) Date: Mon, 17 Apr 2023 23:42:10 +0000 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> <6be200cf-f104-dcfa-58b7-7144a29268a5@oracle.com> Message-ID: > On 17 Apr 2023, at 16:34, Andrew Evers wrote: > > > I would say that it retains the signature of main() exactly as it has been for 20+ years, and allows an additional stage of using imports prior to moving to the full class syntax. It's at most 13 bytes shorter, hardly noticeable. JEP 445 also support imports and also retains the signature of main. It just accepts a few more signatures, and also allows an instance main to process command-line arguments. > > I think it would behoove the group to find a way for import statements to be allowed. Imports are allowed. Can you say which part of the JEP led you to believe they aren?t? Maybe we need to clarify something. ? Ron From andrew.evers at gmail.com Tue Apr 18 00:22:03 2023 From: andrew.evers at gmail.com (Andrew Evers) Date: Mon, 17 Apr 2023 20:22:03 -0400 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> <6be200cf-f104-dcfa-58b7-7144a29268a5@oracle.com> Message-ID: > > I think it would behoove the group to find a way for import statements to be allowed. > > Imports are allowed. Can you say which part of the JEP led you to believe they aren?t? Maybe we need to clarify something. The JEPs mention import four times. The first implies it might be possible, the second in the current launch protocol, and the other two prefaced by "excluding". If there was an example that included an import statement, it would help a lot. On Mon, Apr 17, 2023 at 7:42?PM Ron Pressler wrote: > > > > On 17 Apr 2023, at 16:34, Andrew Evers wrote: > > > > > > I would say that it retains the signature of main() exactly as it has > been for 20+ years, and allows an additional stage of using imports prior > to moving to the full class syntax. It's at most 13 bytes shorter, hardly > noticeable. > > JEP 445 also support imports and also retains the signature of main. It > just accepts a few more signatures, and also allows an instance main to > process command-line arguments. > > > > > I think it would behoove the group to find a way for import statements > to be allowed. > > Imports are allowed. Can you say which part of the JEP led you to believe > they aren?t? Maybe we need to clarify something. > > ? Ron -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Tue Apr 18 04:48:15 2023 From: redio.development at gmail.com (Red IO) Date: Tue, 18 Apr 2023 06:48:15 +0200 Subject: New candidate JEP: 445: Flexible Main Methods and Anonymous Main Classes (Preview) In-Reply-To: References: <9acb6528-2963-463b-c9ea-dc07c5592561@oracle.com> <6be200cf-f104-dcfa-58b7-7144a29268a5@oracle.com> Message-ID: I think it won't be a problem to allow imports in an anonymous class body as the "import xyz;" syntax isn't used by anything that could make this ambiguous. For the main method I think we have 2 valid paths to choose here. 1. The whole file is main and we allow methods in methods meaning that the files content is practically a static main method in an anonymous class. (C# style) 2. We keep the main method as a method static or non static with return type (void, int) "main" parameters (nothing or args) and the main block. (currently proposed) Personally I prefer the later as it still provides a visual marker for the entry point and prevents confusion when for example a beginner switches from anonymous to a named class and wonders why he can't write statements in the class body. The difference between class scope and method scope is important to understand. A bit of topic but I would love to see methods in methods and statics in methods allowed regardless of the "main" topic as they are technically possible (using an inner class in the method) but have huge syntactic overhead due to the unnecessary inner class. Great regards RedIODev On Tue, Apr 18, 2023, 02:23 Andrew Evers wrote: > > > I think it would behoove the group to find a way for import statements > to be allowed. > > > > Imports are allowed. Can you say which part of the JEP led you to > believe they aren?t? Maybe we need to clarify something. > > The JEPs mention import four times. The first implies it might be > possible, the second in the current launch protocol, and the other two > prefaced by "excluding". > > If there was an example that included an import statement, it would help a > lot. > > > On Mon, Apr 17, 2023 at 7:42?PM Ron Pressler > wrote: > >> >> >> > On 17 Apr 2023, at 16:34, Andrew Evers wrote: >> > >> > >> > I would say that it retains the signature of main() exactly as it has >> been for 20+ years, and allows an additional stage of using imports prior >> to moving to the full class syntax. It's at most 13 bytes shorter, hardly >> noticeable. >> >> JEP 445 also support imports and also retains the signature of main. It >> just accepts a few more signatures, and also allows an instance main to >> process command-line arguments. >> >> > >> > I think it would behoove the group to find a way for import statements >> to be allowed. >> >> Imports are allowed. Can you say which part of the JEP led you to believe >> they aren?t? Maybe we need to clarify something. >> >> ? Ron > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Tue Apr 18 13:05:10 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Tue, 18 Apr 2023 13:05:10 +0000 Subject: Apply markdown formatting to the GitHub docs README In-Reply-To: References: Message-ID: <9323A051-3D6B-4E1A-BB3A-54242561BD35@oracle.com> Sorry - that must have slipped through the cracks. Now merged. Gavin > On 10 Apr 2023, at 22:52, Tesla Zhang wrote: > > Hi Amber developers, > > I have created a pull request on GitHub https://github.com/openjdk/amber-docs/pull/17 to apply markdown formatting for the readme file, and it's getting no attention for almost a year. > > I wish that this can be merged :) if I am missing something in the PR, please let me know! > > Best regards, > Tesla From duke at openjdk.org Tue Apr 18 13:05:12 2023 From: duke at openjdk.org (duke) Date: Tue, 18 Apr 2023 13:05:12 GMT Subject: git: openjdk/amber-docs: Apply markdown formatting on GitHub (#17) Message-ID: Changeset: 12b76d4d Author: Tesla Zhang? Committer: GitHub Date: 2023-04-18 09:03:52 +0000 URL: https://git.openjdk.org/amber-docs/commit/12b76d4d269a0d48ee87130d119322dd673d988e Apply markdown formatting on GitHub (#17) = README.md From gbierman at openjdk.org Tue Apr 18 13:06:10 2023 From: gbierman at openjdk.org (Gavin Bierman) Date: Tue, 18 Apr 2023 13:06:10 GMT Subject: [amber-docs] Withdrawn: Apply markdown formatting on GitHub In-Reply-To: References: Message-ID: On Sun, 29 May 2022 21:53:14 GMT, Tesla Zhang? wrote: > Renamed README to README.md so the file will be displayed with markdown rendering on GitHub. This pull request has been closed without being integrated. ------------- PR: https://git.openjdk.org/amber-docs/pull/17 From mark.reinhold at oracle.com Wed Apr 19 14:35:31 2023 From: mark.reinhold at oracle.com (Mark Reinhold) Date: Wed, 19 Apr 2023 14:35:31 +0000 Subject: New candidate JEP: 447: Statements before super() Message-ID: <20230419143530.CC2FA5D28E9@eggemoggin.niobe.net> https://openjdk.org/jeps/447 Summary: In constructors in the Java programming language, allow statements that do not reference the instance being created to appear before this() or super(). - Mark From pedro.lamarao at prodist.com.br Wed Apr 19 14:57:03 2023 From: pedro.lamarao at prodist.com.br (=?UTF-8?Q?Pedro_Lamar=C3=A3o?=) Date: Wed, 19 Apr 2023 11:57:03 -0300 Subject: New candidate JEP: 447: Statements before super() In-Reply-To: <20230419143530.CC2FA5D28E9@eggemoggin.niobe.net> References: <20230419143530.CC2FA5D28E9@eggemoggin.niobe.net> Message-ID: Thank to everyone involved for this effort! In the first fail fast example, the presumably void return type is missing: // This logic really belongs in the constructor private static verifyPositive(long value) { if (value <= 0) throw new IllegalArgumentException("non-positive value"); } Em qua., 19 de abr. de 2023 ?s 11:36, Mark Reinhold < mark.reinhold at oracle.com> escreveu: > https://openjdk.org/jeps/447 > > Summary: In constructors in the Java programming language, allow > statements that do not reference the instance being created to appear > before this() or super(). > > - Mark -- Pedro Lamar?o https://www.prodist.com.br Securing Critical Systems Tel: +55 11 4380-6585 Antes de imprimir esta mensagem e seus anexos, certifique-se que seja realmente necess?rio. Proteger o meio ambiente ? nosso dever. Before printing this e-mail or attachments, be sure it is necessary. It is in our hands to protect the environment. -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Wed Apr 19 16:32:00 2023 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Wed, 19 Apr 2023 11:32:00 -0500 Subject: New candidate JEP: 447: Statements before super() In-Reply-To: References: <20230419143530.CC2FA5D28E9@eggemoggin.niobe.net> Message-ID: Hi Pedro, On Wed, Apr 19, 2023 at 9:57?AM Pedro Lamar?o wrote: > In the first fail fast example, the presumably void return type is missing: > Thanks for catching that - fixed now. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From john.r.rose at oracle.com Thu Apr 20 01:52:41 2023 From: john.r.rose at oracle.com (John Rose) Date: Wed, 19 Apr 2023 18:52:41 -0700 Subject: Bimodal compilation In-Reply-To: <795367426.30059295.1680728164210.JavaMail.zimbra@univ-eiffel.fr> References: <795367426.30059295.1680728164210.JavaMail.zimbra@univ-eiffel.fr> Message-ID: If the method handle in question is a constant, I think a simple inline heuristic is in order here. A constant method handle should be treated as inlined code, if at all possible, not compiled separately. If the method handle in question is a quasi-constant (say, a MCS state) then the problem is harder. But I have heard the pattern match code has a cache in it, and that leads to all sorts ?modality bugs?. I think the answer is to build a (mostly or fully) deterministic algorithm and embody that as a constant method handle, fixing the inlining bug if necessary. On 5 Apr 2023, at 13:56, Remi Forax wrote: > Hi all, > for Devoxx France, me and Jos? Paumard are giving a talk about Valhalla and Amber with several benchmarks mixing the two. > > One problem we have is that the way pattern matching is compiled by javac and later JIT compiled leads to bimodal performance. > Depending on the day (exactly, depending on the JIT threads scheduling), either the method containing the switch is compiled as a whole method (good day) or only the method handle (corresponding to the invokedynamic) is compiled and when the whole method is compiled, the assembly code corresponding to the method handle is considered as too big thus not inlined (bad day). > > The example is using arrays of non null instance of value classes with a default instance (the kind of value classes that are flattened in memory) and a sealed interface, if the method is not fully inlined (good day) performance have very good and if not performance are terrible (bad day). > > A cascade of instanceof while slightly less fast does not exhibit that issue. > > In our example, this bimodal performance issue is hidden when using identity classes, because of the cache misses become the bottleneck. > > I'm a little worry here because I do not see how to fix this bug without changing the way switch on types are compiled by javac, so this problem has to be tackled before the JDK 21 is released otherwise, people will have to recompile their application to fix that bug. > > regards, > R?mi > > --- > > public @ZeroDefault @Value record Population(int amount) { > public static Population zero() { > return new Population(0); > } > public Population add(Population other) { > return new Population(this.amount + other.amount); > } > } > public sealed interface Populated permits City, Department, Region {} > public @ZeroDefault @Value record City(String name, @NonNull Population population) implements Populated {} > public @ZeroDefault @Value record Department(String name, City[] cities) implements Populated { > public Department { > cities = Arrays.stream(cities).toArray(size -> RT.newNonNullArray(City.class, size)); > } > } > public @ZeroDefault @Value record Region(String name, Department[] departments) implements Populated { > public Region { > departments = Arrays.stream(departments).toArray(size -> RT.newNonNullArray(Department.class, size)); > } > } > > public static Population sumPopulationOf(Populated populated) { > return switch (populated) { > case City(var name, var population) -> population; > case Department(var name, var cities) -> { > var sum = Population.zero(); > for(var city: cities) { > sum = sum.add(sumPopulationOf(city)); > } > yield sum; > } > case Region(var name, var departments) -> { > var sum = Population.zero(); > for(var department: departments) { > sum = sum.add(sumPopulationOf(department)); > } > yield sum; > } > }; > } > > public static Population sumPopulationOf(Populated[] populateds) { > var sum = Population.zero(); > for(var populated: populateds) { > sum = sum.add(sumPopulationOf(populated)); > } > return sum; > } > > --- > > public class BenchDOP { > private Region[] regions; > > @Setup > public void init() { > var data = Data.readCities(); > regions = data.regions().toArray(size -> RT.newNonNullArray(Region.class, size)); > } > > @Benchmark > public Population sumPopulations() { > return Data.sumPopulationOf(regions); > } > } > > > --- > > > > # Benchmark: org.paumard.amber.model.cityvaluenonnullablearraydrecords.BenchDOP.sumPopulations > > # Run progress: 0.00% complete, ETA 00:02:06 > # Fork: 1 of 3 > # Warmup Iteration 1: 47.845 us/op > # Warmup Iteration 2: 38.199 us/op > # Warmup Iteration 3: 38.130 us/op > # Warmup Iteration 4: 37.909 us/op > # Warmup Iteration 5: 38.345 us/op > Iteration 1: 38.581 us/op > Iteration 2: 37.946 us/op > Iteration 3: 37.837 us/op > Iteration 4: 38.013 us/op > Iteration 5: 37.885 us/op > Iteration 6: 37.853 us/op > Iteration 7: 37.931 us/op > Iteration 8: 37.874 us/op > Iteration 9: 37.828 us/op > Iteration 10: 37.925 us/op <--- good day > > # Run progress: 8.33% complete, ETA 00:02:00 > # Fork: 2 of 3 > # Warmup Iteration 1: 2871.011 us/op > # Warmup Iteration 2: 2761.856 us/op > # Warmup Iteration 3: 2759.977 us/op > # Warmup Iteration 4: 2761.045 us/op > # Warmup Iteration 5: 2756.167 us/op > Iteration 1: 2755.180 us/op > Iteration 2: 2781.178 us/op > Iteration 3: 2759.068 us/op > Iteration 4: 2755.737 us/op > Iteration 5: 2755.112 us/op > Iteration 6: 2754.553 us/op > Iteration 7: 2761.759 us/op > Iteration 8: 2750.829 us/op > Iteration 9: 2751.265 us/op > Iteration 10: 2749.668 us/op <--- bad day > > # Run progress: 16.67% complete, ETA 00:01:48 > # Fork: 3 of 3 > # Warmup Iteration 1: 42.359 us/op > # Warmup Iteration 2: 38.322 us/op > # Warmup Iteration 3: 38.311 us/op > # Warmup Iteration 4: 37.990 us/op > # Warmup Iteration 5: 37.988 us/op > Iteration 1: 38.139 us/op > Iteration 2: 38.052 us/op > Iteration 3: 37.959 us/op > Iteration 4: 38.037 us/op > Iteration 5: 37.997 us/op > Iteration 6: 37.957 us/op > Iteration 7: 37.977 us/op > Iteration 8: 37.905 us/op > Iteration 9: 37.913 us/op > Iteration 10: 37.976 us/op <--- good day From hjohn at xs4all.nl Thu Apr 20 07:20:34 2023 From: hjohn at xs4all.nl (John Hendrikx) Date: Thu, 20 Apr 2023 07:20:34 +0000 Subject: Variable not in scope after instanceof Message-ID: I came across a bit of a curious way to check if something is not an instance of something, and I noticed it doesn't play along with the new instanceof construct that allows you to assign it to a local variable immediately: public static void main(String[] args) { Number n = 1.0; if (n instanceof Double d == false) { System.out.println("It was not a double"); return; } System.out.println("It was a double: " + d); // compile error } In the above snippet, the variable "d", despite being accepted, is not in scope anywhere. Changing the check to " !(n instanceof Double d)" works as expected. Is this something that was missed, or am I misunderstanding why this can't work? --John -------------- next part -------------- An HTML attachment was scrubbed... URL: From robbepincket at live.be Thu Apr 20 08:03:47 2023 From: robbepincket at live.be (Robbe Pincket) Date: Thu, 20 Apr 2023 08:03:47 +0000 Subject: Variable not in scope after instanceof In-Reply-To: References: Message-ID: Hi John, To be honest I never fully read the rules on when pattern values are in scope or not, and just always assumed it matches my mental model of ?the variable is in scope if it would be definite assigned here assuming the variable is definite assigned iff the match returned true?. Using this assumption it is clear why this snippet wouldn?t work. `==` does not have any special rules about definite assignment when it?s arguments are boolean expressions and therefore falls back to the default of ?A variable is definite assigned after the execution of a boolean expression if it is definite assigned both when true and false? Hope this helps Kind regards Robbe Pincket Van: John Hendrikx Verzonden: donderdag 20 april 2023 9:21 Aan: amber-dev Onderwerp: Variable not in scope after instanceof I came across a bit of a curious way to check if something is not an instance of something, and I noticed it doesn't play along with the new instanceof construct that allows you to assign it to a local variable immediately: public static void main(String[] args) { Number n = 1.0; if (n instanceof Double d == false) { System.out.println("It was not a double"); return; } System.out.println("It was a double: " + d); // compile error } In the above snippet, the variable "d", despite being accepted, is not in scope anywhere. Changing the check to " !(n instanceof Double d)" works as expected. Is this something that was missed, or am I misunderstanding why this can't work? --John -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Apr 20 13:40:12 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 20 Apr 2023 09:40:12 -0400 Subject: Variable not in scope after instanceof In-Reply-To: References: Message-ID: > To be honest I never fully read the rules on when pattern values are > in scope or not, and just always assumed it matches my mental model of > ?the variable is in scope if it would be definite assigned here > assuming the variable is definite assigned iff the match returned true?. > In fact, this was the design rubric: "don't make users learn a new set of rules, just lean on DA." -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Thu Apr 20 21:19:14 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 20 Apr 2023 21:19:14 +0000 Subject: Draft Spec for Statements before Super() (JEP 447) Message-ID: Dear experts: The first draft of the spec change document for the feature Statements before super() [1] is now available at: https://cr.openjdk.org/~gbierman/jep447/latest/ Please give us your feedback (either on this list or directly to me). Thanks, Gavin [1] Statements before super(): https://openjdk.org/jeps/447 -------------- next part -------------- An HTML attachment was scrubbed... URL: From gavin.bierman at oracle.com Thu Apr 20 21:26:17 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 20 Apr 2023 21:26:17 +0000 Subject: [Resend] Draft Spec for Statements before Super() (JEP 447) Message-ID: <7405ACA9-C88D-4170-A758-714B6F9CBD27@oracle.com> [Resending as the hyperlinks got mangled] Dear experts: The first draft of the spec change document for the feature Statements before super() [1] is now available at: https://cr.openjdk.org/~gbierman/jep447/latest/ Please give us your feedback (either on this list or directly to me). Thanks, Gavin [1] Statements before super(): https://openjdk.org/jeps/447 From gavin.bierman at oracle.com Thu Apr 20 21:29:03 2023 From: gavin.bierman at oracle.com (Gavin Bierman) Date: Thu, 20 Apr 2023 21:29:03 +0000 Subject: [Resend] Draft Spec for Statements before Super() (JEP 447) In-Reply-To: <7405ACA9-C88D-4170-A758-714B6F9CBD27@oracle.com> References: <7405ACA9-C88D-4170-A758-714B6F9CBD27@oracle.com> Message-ID: <0A605E48-05AA-4EE2-8861-5E50225A7336@oracle.com> Third time lucky: Dear experts: The first draft of the spec change document for the feature Statements before super() [1] is now available at: https://cr.openjdk.org/~gbierman/jep447/latest/ Please give us your feedback (either on this list or directly to me). Thanks, Gavin [1] Statements before super(): https://openjdk.org/jeps/447 On 20 Apr 2023, at 22:26, Gavin Bierman wrote: [Resending as the hyperlinks got mangled] Dear experts: The first draft of the spec change document for the feature Statements before super() [1] is now available at: https://cr.openjdk.org/~gbierman/jep447/latest/ Please give us your feedback (either on this list or directly to me). Thanks, Gavin [1] Statements before super(): https://openjdk.org/jeps/447 -------------- next part -------------- An HTML attachment was scrubbed... URL: From alex.buckley at oracle.com Thu Apr 20 22:42:21 2023 From: alex.buckley at oracle.com (Alex Buckley) Date: Thu, 20 Apr 2023 15:42:21 -0700 Subject: Draft Spec for Statements before Super() (JEP 447) In-Reply-To: References: Message-ID: <55ea813b-56a5-be2a-41c9-ac921651e628@oracle.com> Hi Gavin, a few comments on a super draft for a super() feature. On 4/20/2023 2:19 PM, Gavin Bierman wrote: > The first draft of the spec change document for the feature Statements > before super() [1] is now available at: > > https://cr.openjdk.org/~gbierman/jep447/latest/ 1. The decision to sever constructor bodies from "static context" is righteous. As the edit in 8.1.3 implies, a static context is properly defined to involve _no_ current instance, versus the traditional definition of there's-a-current-instance-but-you-can't-refer-to-it. It is right to introduce a new kind of context for (some of) a constructor body, where uniquely there's-a-current-instance-but-you-can't-refer-to-it. 2. For the new kind of context, I recommend "pre-construction context" instead of "pre-initialization context". Initialization is what happens to a class prior to any constructors being run. The last paragraph of 15.9 is keen on a class being "instantiated" but I think most people would trip over "pre-instantiation context" and would thank you for the plainspoken "pre-construction context". 3. The new kind of context is introduced as a _dynamic_ artifact -- "... a pre-initialization context *of the current object*" -- but it's sweeping out a lexical space. Even the dynamic story about new instances in 12.5 doesn't mention the pre-initialization context *of the new instance*. Wouldn't the following pattern work throughout: "It is a compile-time error if a this expression occurs in a static context (8.1.3) or in ~a~ +the+ pre-initialization context of ~the associated instance~ +a constructor body+ (8.8.7.1)." 4. In 8.8.7.1, you mention "An explicit constructor invocation *statement* ...". It would be worth noting in 8.8.7 that an explicit constructor invocation is not a statement by the technical meaning of 14.5, yet shares most of the characteristics of statements such as (i) having normal and abrupt completion, and (ii) potentially being unreachable by 14.22. (You ban `return` in the prologue, but see below for a notional ctor body that I'm certain javac would sensibly reject for an unreachable `super();` "statement".) ``` P: { System.out.println("ctor started"); break P; super(); } System.out.println("ctor finished"); ``` Alex From davidalayachew at gmail.com Fri Apr 21 03:29:22 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 20 Apr 2023 23:29:22 -0400 Subject: Quick questions about parallelism and some amber features Message-ID: Hello Amber Dev Team, Quick questions about some amber features and parallelism. 1. For switch expressions, do they ever use parallelism to evaluate the branches? As in, check multiple branches simultaneously? 2. What about record deconstruction patterns? I know the class ones that we are going to write can be up to us. But do record patterns ever do that? Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Fri Apr 21 20:55:06 2023 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Fri, 21 Apr 2023 15:55:06 -0500 Subject: Draft Spec for Statements before Super() (JEP 447) In-Reply-To: <55ea813b-56a5-be2a-41c9-ac921651e628@oracle.com> References: <55ea813b-56a5-be2a-41c9-ac921651e628@oracle.com> Message-ID: Hi Alex, Great comments - thanks. I like "pre-construction context" better also. On Thu, Apr 20, 2023 at 5:42?PM Alex Buckley wrote: > You ban `return` in the prologue, but see below > for a notional ctor body that I'm certain javac would sensibly reject > for an unreachable `super();` "statement".) > > ``` > P: { > System.out.println("ctor started"); > break P; > super(); > } > System.out.println("ctor finished"); > ``` > Maybe we need a parenthetical comment that points out how any nesting of a super()/this() call inside a block or anything else is disallowed by the language production for *ConstructorBody*, in 8.8.7, which effectively requires *ExplicitConstructorInvocation* to be top level. -Archie -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From steve at ethx.net Fri Apr 21 21:17:40 2023 From: steve at ethx.net (Steve Barham) Date: Fri, 21 Apr 2023 22:17:40 +0100 Subject: Quick questions about parallelism and some amber features In-Reply-To: References: Message-ID: Hi David, It feels to me that Boolean short circuiting in conditionals and statement ordering in switches would both preclude parallel evaluation of conditions, absent some infective constery to wave away side effects. In any event - do you envisage any benefit from parallel evaluation? On such scales would runtime cost not be dominated by dispatch and synchronisation? Steve On Fri, 21 Apr 2023 at 04:31, David Alayachew wrote: > Hello Amber Dev Team, > > Quick questions about some amber features and parallelism. > > 1. For switch expressions, do they ever use parallelism to evaluate the > branches? As in, check multiple branches simultaneously? > 2. What about record deconstruction patterns? I know the class ones that > we are going to write can be up to us. But do record patterns ever do that? > > Thank you for your time! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Apr 22 01:15:43 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 21 Apr 2023 21:15:43 -0400 Subject: Quick questions about parallelism and some amber features In-Reply-To: References: Message-ID: Hello Steve, Thank you for your response! > It feels to me that Boolean short circuiting in > conditionals and statement ordering in switches would > both preclude parallel evaluation of conditions I'm actually expecting you to be correct here. The reason I originally asked this question is because I have been doing a lot of study into when and where parallelism starts to become useful when coding in Java. Long story short, it improves performance way earlier than I ever expected. And not only that, Java actually does a whole bunch of parallelism itself under the hood too. Compilation, class loading, etc. I thought I knew the threshold at which parallelism would start being useful, but apparently it's way earlier than I thought. > absent some infective constery to wave away side effects Google does not seem to be much help. What does this quote mean? > In any event - do you envisage any benefit from parallel > evaluation? On such scales would runtime cost not be > dominated by dispatch and synchronisation? Well at this point, I have no idea. I don't really know where the ceiling is anymore, so I am testing out (what I think is) a complete long shot, just to give myself an upper bound here. Worst case is they say no, which is what I am expecting. Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From sarma.swaranga at gmail.com Sun Apr 23 11:19:23 2023 From: sarma.swaranga at gmail.com (Swaranga Sarma) Date: Sun, 23 Apr 2023 04:19:23 -0700 Subject: Pattern matching on multiple objects Message-ID: I was recently writing a simple state machine where the machine state has simple deterministic rules for the state transitions given the current state and the input. I felt both the state and the input types lent themselves well to sealed classes and records but while implementing the logic I had to resort to if-else statements. At that point, I felt if I were able to switch and pattern-match on both the input types together as a tuple, my logic would have been much clearer while also concise. Simplified sample code that I wish to be able to write (although the point is not the exact syntax): sealed interface State {} record Start() implements State {} record First() implements State {} record Second() implements State {} record End() implements State {} sealed interface Move {} record Jump(int steps) implements Move { Jump { if (steps < 0 || steps > 2) throw new IllegalArgumentException(); } } record None() implements Move {} State transition(State initial, Move move) { return switch(initial, move) { case (Start, Jump(1)) -> First; case (Start, Jump(2)) | (First, Jump(1)) -> Second; case (First, Jump(2)) | (Second, Jump(_)) -> End; case (End, _) -> End; case (None, _) -> initial; } } I did not see in the amber docs about being able to use multiple patterns in a switch expression in any of the future plans. It does not even have to be a switch really but something to allow this type of expressivity. This feels very much in line with the Data Oriented programming article published a while ago. Even if something like this were possible, I am guessing a default clause would be needed in the switch because the compiler cannot infer that Jump(1) and Jump(2) are the only valid jumps. Being able to help the compiler by expressing such constraints at the language level would be even more amazing but that is a whole other topic. Would something like this be possible one day? Regards Swaranga -------------- next part -------------- An HTML attachment was scrubbed... URL: From me at tylerkindy.com Sun Apr 23 11:39:41 2023 From: me at tylerkindy.com (Tyler Kindy) Date: Sun, 23 Apr 2023 07:39:41 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: <6765DE15-12DB-485D-B554-39CC6F32B76C@tylerkindy.com> > At that point, I felt if I were able to switch and pattern-match on both the input types together as a tuple, my logic would have been much clearer while also concise. You could create a new record that holds both the current state and the move. Then you could use a single record pattern to switch over the combination of the two. Tyler Kindy > On Apr 23, 2023, at 7:20 AM, Swaranga Sarma wrote: > > ? > I was recently writing a simple state machine where the machine state has simple deterministic rules for the state transitions given the current state and the input. I felt both the state and the input types lent themselves well to sealed classes and records but while implementing the logic I had to resort to if-else statements. At that point, I felt if I were able to switch and pattern-match on both the input types together as a tuple, my logic would have been much clearer while also concise. > > Simplified sample code that I wish to be able to write (although the point is not the exact syntax): > sealed interface State {} > record Start() implements State {} > record First() implements State {} > record Second() implements State {} > record End() implements State {} > > sealed interface Move {} > record Jump(int steps) implements Move { > Jump { > if (steps < 0 || steps > 2) > throw new IllegalArgumentException(); > } > } > record None() implements Move {} > > State transition(State initial, Move move) { > return switch(initial, move) { > case (Start, Jump(1)) -> First; > case (Start, Jump(2)) | (First, Jump(1)) -> Second; > case (First, Jump(2)) | (Second, Jump(_)) -> End; > case (End, _) -> End; > case (None, _) -> initial; > } > } > > I did not see in the amber docs about being able to use multiple patterns in a switch expression in any of the future plans. It does not even have to be a switch really but something to allow this type of expressivity. This feels very much in line with the Data Oriented programming article published a while ago. > > Even if something like this were possible, I am guessing a default clause would be needed in the switch because the compiler cannot infer that Jump(1) and Jump(2) are the only valid jumps. Being able to help the compiler by expressing such constraints at the language level would be even more amazing but that is a whole other topic. > > Would something like this be possible one day? > > Regards > Swaranga -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Apr 23 15:25:56 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 23 Apr 2023 11:25:56 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: <21770f8d-a3a9-143d-7bd6-01f80b52f812@oracle.com> There was some brief mention in an earlier document about this as a possible long-term future feature. In functional languages, this comes mostly for free because the selector expression (initial, move) is just a tuple, and "tuple patterns" are just a trivial structural lifting of patterns. Your state machine example is nice in that it exposes the transition table directly in the language, but the real win here is FizzBuzz :) ??? String fizzBuzz(int i) { ? ?? ?? return switch (i % 5, i % 3) { ??????????? case (0, 0) -> "FizzBuzz"; ??????????? case (0, _) -> "Fizz"; ??????????? case (_, 0) -> "Buzz"; ??????????? default -> String.valueOf(i); ??? } In Java, this would be an extension to switch, where a selector could be a sequence of values (like an argument list), and the cases would be sequences of patterns. So the answer is maybe, someday. On 4/23/2023 7:19 AM, Swaranga Sarma wrote: > I was recently writing a simple state machine where the machine state > has simple deterministic rules for the state transitions given the > current state and the input. I felt both the state and the input types > lent themselves well to sealed classes and records but while > implementing the logic I had to resort to if-else statements. At that > point, I felt if I were able to switch and pattern-match on both the > input types together as a tuple, my logic would have been much clearer > while also concise. > > Simplified sample code that I wish to be able to write (although the > point is not the exact syntax): > sealed interface State {} record Start() implements State {} record > First() implements State {} record Second() implements State {} record > End() implements State {} sealed interface Move {} record Jump(int > steps) implements Move { Jump { if (steps < 0 || steps > 2) throw new > IllegalArgumentException(); } } record None() implements Move {} State > transition(State initial, Move move) { return switch(initial, move) { > case (Start, Jump(1)) -> First; case (Start, Jump(2)) | (First, > Jump(1)) -> Second; case (First, Jump(2)) | (Second, Jump(_)) -> End; > case (End, _) -> End; case (None, _) -> initial; } } > > I did not see in the amber docs about being able to use multiple > patterns in a switch expression in any of the future plans. It does > not even have to be a switch really but something to allow this type > of expressivity. This feels very much in line with the Data Oriented > programming article published a while ago. > > Even if something like this were possible, I am guessing a default > clause would be needed in the switch because the compiler cannot infer > that Jump(1) and Jump(2) are the only valid jumps. Being able to help > the compiler? by expressing such constraints at the language level > would be even more amazing but that is a whole other topic. > > Would something like this be possible one day? > > Regards > Swaranga -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 23 17:18:53 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 23 Apr 2023 13:18:53 -0400 Subject: Pattern matching on multiple objects Message-ID: Hello, For whatever reason, I remember seeing this as another potential future direction for Java. $ build/windows-x86_64-server-release/images/jdk/bin/jshell --enable-preview | Welcome to JShell -- Version 21-internal | For an introduction type: /help intro jshell> sealed interface ABC { ...> enum A implements ABC {Z} ...> record B(A a) implements ABC {} ...> record C(A a, B b) implements ABC {} ...> } | created interface ABC jshell> final ToIntFunction d = ...> abc -> ...> switch (abc) { ...> case C(A.Z, B(A.Z)) -> 1; ...> default -> 0; ...> }; I think it was called constant patterns? Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 23 17:23:28 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 23 Apr 2023 13:23:28 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: And to explain further, the constant pattern could also be a Number instead of an enum. I also think there were mentions of using existing instances as well? If this were possible, we could follow Tyler's suggestion, wrap the values in a record, then destructure like normal, only using a "constant pattern" instead. -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Apr 23 18:00:18 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 23 Apr 2023 14:00:18 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: Yes, many languages that have pattern matching allow constant literals to be used as patterns ("constant patterns"), so ??? case Point(0, _): would match any point on the X axis.? The example Swaranga gave used constant patterns in this way, but that is orthogonal to the question being posed, which was about switching over multiple values at once. Constant patterns have been discussed in early pattern matching writeups; we have left them on the "maybe for later" list, for a few reasons (and I'm glad we did.)? They may come into the lineup at some point, but in the meantime, there are higher-leverage features ahead of it. On 4/23/2023 1:18 PM, David Alayachew wrote: > Hello, > > For whatever reason, I remember seeing this as another potential > future direction for Java. > > $ build/windows-x86_64-server-release/images/jdk/bin/jshell > --enable-preview > | ?Welcome to JShell -- Version 21-internal > | ?For an introduction type: /help intro > > jshell> sealed interface ABC { > ? ?...> ? ? enum A implements ABC {Z} > ? ?...> ? ? record B(A a) implements ABC {} > ? ?...> ? ? record C(A a, B b) implements ABC {} > ? ?...> } > | ?created interface ABC > > jshell> final ToIntFunction d = > ? ?...> abc -> > ? ?...> switch (abc) { > ? ?...> ? ? case C(A.Z, B(A.Z)) -> 1; > ? ?...> ? ? default -> 0; > ? ?...> }; > > I think it was called constant patterns? > > Thank you for your time! > David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Sun Apr 23 18:20:11 2023 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Sun, 23 Apr 2023 21:20:11 +0300 Subject: Quick questions about parallelism and some amber features In-Reply-To: References: Message-ID: I don?t see how you can have parallelism without having some horrible potential bugs. Imagine the following situation: public static AtomicBoolean bl = new AtomicBoolean(true);public static void sideEffect() { bl.set(false); return true; } Integer i = 0;switch (i) { case Integer i when bl.get() -> ... case Integer i when sideEffect() -> ... ... } In this example we can have: - Enter case 1 with correct state - Enter case 2 with correct state - Enter case 1 with wrong state Using move variables we can create a (albeit a bit pathological) example with any number of cases that all can be entered with a wrong state (apart from maybe the default branch). Also, it can be dangerous when the when clause is expensive. Maybe it is possible to calculate the non-when clause in parallel fashion, but I don?t know if it worth the effort On Sat, Apr 22, 2023 at 4:16?AM David Alayachew wrote: > > Hello Steve, > > Thank you for your response! > > > It feels to me that Boolean short circuiting in > > conditionals and statement ordering in switches would > > both preclude parallel evaluation of conditions > > I'm actually expecting you to be correct here. > > The reason I originally asked this question is because I have been doing a > lot of study into when and where parallelism starts to become useful when > coding in Java. Long story short, it improves performance way earlier than > I ever expected. And not only that, Java actually does a whole bunch of > parallelism itself under the hood too. Compilation, class loading, etc. I > thought I knew the threshold at which parallelism would start being useful, > but apparently it's way earlier than I thought. > > > absent some infective constery to wave away side effects > > Google does not seem to be much help. What does this quote mean? > > > In any event - do you envisage any benefit from parallel > > evaluation? On such scales would runtime cost not be > > dominated by dispatch and synchronisation? > > Well at this point, I have no idea. I don't really know where the ceiling > is anymore, so I am testing out (what I think is) a complete long shot, > just to give myself an upper bound here. Worst case is they say no, which > is what I am expecting. > > Thank you for your time! > David Alayachew > > -- Holo The Wise Wolf Of Yoitsu -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 23 18:40:33 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 23 Apr 2023 14:40:33 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: Hello Brian, Thanks for the response! > Yes, many languages that have pattern matching allow > constant literals to be used as patterns ("constant > patterns"), so > > case Point(0, _): > > would match any point on the X axis. Nice, the unnamed pattern makes this even more expressive than I recalled. Very haskell-esque. > The example Swaranga gave used constant patterns in this > way, but that is orthogonal to the question being posed, > which was about switching over multiple values at once. Fair. Next time, I will start a separate thread and just reference the source thread. > Constant patterns have been discussed in early pattern > matching writeups; we have left them on the "maybe for > later" list, for a few reasons (and I'm glad we did.) If you recall the reasons, I'd love to hear them. > They may come into the lineup at some point, but in the > meantime, there are higher-leverage features ahead of it. Same goes for this too. What's the amber team working on at the moment? Thank you for your time! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 23 19:04:29 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 23 Apr 2023 15:04:29 -0400 Subject: Quick questions about parallelism and some amber features In-Reply-To: References: Message-ID: Hello Holo, Thank you for your response! > I don?t see how you can have parallelism without having > some horrible potential bugs. > Imagine the following situation: > > public static AtomicBoolean bl = new AtomicBoolean(true); > public static void sideEffect() { > bl.set(false); > return true; > } > ... > Integer i = 0; > switch (i) { > case Integer i when bl.get() -> ... > case Integer i when sideEffect() -> ... > ... > } > > In this example we can have: > > - Enter case 1 with correct state > - Enter case 2 with correct state > - Enter case 1 with wrong state > > Using move variables we can create a (albeit a bit > pathological) example with any number of cases that all > can be entered with a wrong state (apart from maybe the > default branch). Yes, no matter what, side effects can cause all of this to fall apart. Though, I would say that that already sounds like a pretty severe code smell, if not a bug (with the exception of maybe execution counters or something like that). And that's excluding parallelism here, just plain sequential execution of the cases. > Also, it can be dangerous when the when clause is > expensive. Maybe it is possible to calculate the non-when > clause in parallel fashion, but I don?t know if it worth > the effort I see what you mean. An extremely expensive when clause that should be called only when all other cases fail would cause pretty severe performance problems for no good reason. And realistically, the same could be said of the deconstruction pattern too. Now that user created deconstruction patterns are coming down the pipe, that technically means that deconstruction could be expensive too. Furthermore, reading your comment just now made me think of a problematic example. final int someInt = 20; final String response = switch (someInt) { case Integer i when i >= 0 && i < 10 -> "a"; case Integer i when i >= 10 && i <=20 -> "b"; case Integer i when i >= 20 && i < 30 -> "c"; case null, default -> throw new IllegalArgumentException("?!"); }; Sequential execution will always return "b", but parallel execution might return "c", assuming no clean up effort by Java to return the "top-most case". In short, you raised a pretty definitive point in why concurrent execution would be problematic. And Steve mentioned earlier in this thread that the overhead would be too much, so I definitely see what you mean. And to be clear, my OP and subsequent responses are all purely with knowledge gathering intent. I want to learn more about how parallelism is used in Java. This is certainly no feature request or "feasability request". Thank you for your insight! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Apr 23 19:35:50 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 23 Apr 2023 15:35:50 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: > > > They may come into the lineup at some point, but in the > > meantime, there are higher-leverage features ahead of it. > > Same goes for this too. What's the amber team working on at the moment? The next target is deconstruction patterns for classes (and interfaces).? This will open up some powerful new API design idioms, plus lay groundwork for additional language features such as `with` expressions. -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 23 19:37:59 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 23 Apr 2023 15:37:59 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: Thanks Brian, Unrelated question while I have you here, I saw a video a while back that said a valuable contribution to the mailing list is trying out new features and posting experience. Which features in amber are in need of that right now? -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Sun Apr 23 19:39:41 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 23 Apr 2023 15:39:41 -0400 Subject: Pattern matching on multiple objects In-Reply-To: References: Message-ID: <76eca9ef-dfa1-2d80-5282-82149f31ceee@oracle.com> Anything that is in EA or preview is fair game -- that is literally the point of preview features.? While we try hard to flesh out the "how might people use/misuse it" ahead of time, having people use it in real, nontrivial situations can sometimes shed additional light. On 4/23/2023 3:37 PM, David Alayachew wrote: > Thanks Brian, > > Unrelated question while I have you here, I saw a video a while back > that said a valuable contribution to the mailing list is trying out > new features and posting experience. Which features in amber are in > need of that right now? > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Sun Apr 23 19:49:43 2023 From: forax at univ-mlv.fr (Remi Forax) Date: Sun, 23 Apr 2023 21:49:43 +0200 (CEST) Subject: Pattern matching on multiple objects In-Reply-To: <21770f8d-a3a9-143d-7bd6-01f80b52f812@oracle.com> References: <21770f8d-a3a9-143d-7bd6-01f80b52f812@oracle.com> Message-ID: <1438599118.41183980.1682279383993.JavaMail.zimbra@univ-eiffel.fr> > From: "Brian Goetz" > To: "Swaranga Sarma" , "amber-dev" > > Sent: Sunday, April 23, 2023 5:25:56 PM > Subject: Re: Pattern matching on multiple objects > There was some brief mention in an earlier document about this as a possible > long-term future feature. > In functional languages, this comes mostly for free because the selector > expression (initial, move) is just a tuple, and "tuple patterns" are just a > trivial structural lifting of patterns. Your state machine example is nice in > that it exposes the transition table directly in the language, but the real win > here is FizzBuzz :) > String fizzBuzz(int i) { > return switch (i % 5, i % 3) { > case (0, 0) -> "FizzBuzz"; > case (0, _) -> "Fizz"; > case (_, 0) -> "Buzz"; > default -> String.valueOf(i); > } > In Java, this would be an extension to switch, where a selector could be a > sequence of values (like an argument list), and the cases would be sequences of > patterns. > So the answer is maybe, someday. and a tuple be simulated by a local record, "_" can be simulated by a "var __" and a constant pattern can be simulated by a guard (the "when"), static String fizzBuzz(int i) { record $(int d5, int d3) {} return switch (new $(i % 5, i % 3)) { case $(var d5, var d3) when d5 == 0 && d3 == 0 -> "FizzBuzz"; case $(var d5, var __) when d5 == 0 -> "Fizz"; case $(var __, var d3) when d3 == 0 -> "Buzz"; default -> "" + i; }; } regards, R?mi > On 4/23/2023 7:19 AM, Swaranga Sarma wrote: >> I was recently writing a simple state machine where the machine state has simple >> deterministic rules for the state transitions given the current state and the >> input. I felt both the state and the input types lent themselves well to sealed >> classes and records but while implementing the logic I had to resort to if-else >> statements. At that point, I felt if I were able to switch and pattern-match on >> both the input types together as a tuple, my logic would have been much clearer >> while also concise. >> Simplified sample code that I wish to be able to write (although the point is >> not the exact syntax): >> sealed interface State {} record Start() implements State {} record First() >> implements State {} record Second() implements State {} record End() implements >> State {} sealed interface Move {} record Jump( int steps) implements Move { >> Jump { if (steps < 0 || steps > 2 ) throw new IllegalArgumentException(); } } >> record None() implements Move {} State transition (State initial, Move move) { >> return switch (initial, move) { case (Start, Jump( 1 )) -> First; case (Start, >> Jump( 2 )) | (First, Jump( 1 )) -> Second; case (First, Jump( 2 )) | (Second, >> Jump(_)) -> End; case (End, _) -> End; case (None, _) -> initial; } } >> I did not see in the amber docs about being able to use multiple patterns in a >> switch expression in any of the future plans. It does not even have to be a >> switch really but something to allow this type of expressivity. This feels very >> much in line with the Data Oriented programming article published a while ago. >> Even if something like this were possible, I am guessing a default clause would >> be needed in the switch because the compiler cannot infer that Jump(1) and >> Jump(2) are the only valid jumps. Being able to help the compiler by expressing >> such constraints at the language level would be even more amazing but that is a >> whole other topic. >> Would something like this be possible one day? >> Regards >> Swaranga -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sun Apr 23 20:11:18 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Sun, 23 Apr 2023 16:11:18 -0400 Subject: Pattern matching on multiple objects In-Reply-To: <76eca9ef-dfa1-2d80-5282-82149f31ceee@oracle.com> References: <76eca9ef-dfa1-2d80-5282-82149f31ceee@oracle.com> Message-ID: Much appreciated! -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Mon Apr 24 15:42:38 2023 From: duke at openjdk.org (duke) Date: Mon, 24 Apr 2023 15:42:38 GMT Subject: git: openjdk/amber-docs: Update to include JEP 447. Message-ID: Changeset: 0a65d505 Author: Archie L. Cobbs Date: 2023-04-24 10:38:59 +0000 URL: https://git.openjdk.org/amber-docs/commit/0a65d50583599f69e82d59e5d5ccf5abd3d43b66 Update to include JEP 447. ! site/_index.md From duke at openjdk.org Mon Apr 24 17:00:31 2023 From: duke at openjdk.org (duke) Date: Mon, 24 Apr 2023 17:00:31 GMT Subject: git: openjdk/amber: super-init: Use for() loop instead of stream for efficiency. Message-ID: Changeset: 60892aec Author: Archie L. Cobbs Date: 2023-03-20 14:12:45 +0000 URL: https://git.openjdk.org/amber/commit/60892aec5dfd3fe9d10322181811657592f2e501 Use for() loop instead of stream for efficiency. ! src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Lower.java From davidalayachew at gmail.com Wed Apr 26 07:02:53 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Wed, 26 Apr 2023 03:02:53 -0400 Subject: My experience building a path-finding algorithm using Amber Message-ID: Hello Amber Dev Team, I just wanted to share my experience with some of the new/preview Amber features such as Sealed Types, Data-Oriented Programming, and Pattern-Matching. Specifically, I wanted to show how things turned out when I used them in a project. This project was built from the ground up to use all of the Amber features from Java 20 (and prior) to their fullest extent. If you want an in-depth look at the project itself, here is a link to the GitHub project. If you clone it, you can run it right now using Java 21 + preview features. https://github.com/davidalayachew/HelltakerPathFinder/tree/a5c8757425c468288a2021e533ac31958f89360f The project I built is essentially a Path Finding Algorithm, built for the video game Helltaker (free on Steam if you want to understand the code a little better, but Content Warning -- Occult/Violence/Lewd). The project is functionally complete, except for the DLC. Other than that, I am just adding performance improvements. For example, I am in the middle of adding parallelism to the search algorithm. Please ignore the CompletableFuture::completedStage calls in the code, those are placeholder for actual parallelism soon to come later. Here is a link to a rundown of the game's mechanics [1]. On to the code. I already had a lot of practice using Sealed Types and Data-Oriented Programming [2], so I took my lessons learned from that to make this project work better than last time. To avoid spending all of my time repeating myself from last time, let me quickly sum up the parts that were the same as last time. * Sealed types, records, and switch expressions make refactoring easy and borderline painless. I am still in shock and awe about how quickly I can uproot and replace entire sections of my code base with very little effort. And the error messages point me to exactly what needs to change, leading me down the right path like guard rails, as opposed to being thrown around to different solutions like a pinball machine. * Converting untyped data into typed data still feels frustrating and difficult. I was able to bypass most of it this time around because, thankfully, this project had very little conversion (whereas my previous project [2] was completely built around that concept) On to the new stuff. This time around, I added pattern matching to my code. Specifically, record patterns and switch patterns. These 2 features were critical to this project's success because, this time around, I had a very complex hierarchy of types that was not only wide, but deep. For example, I made a sealed interface Cell [3] to model the state of each Cell on my grid. Then, I broke that down into more sealed interfaces, records, and enums. Since all elements of this structure were either sealed, a record, or an enum, that meant that my domain of possible types (and thus, solutions) was very bounded. That boundedness made reasoning about my domain much easier. And it's a good thing it was easy because the possible number of states I had to reason through was extremely large. For example, when building the actual path finding algorithm, I needed to exhaustively handle all possible states that my cells could be. And worse yet, I need to pattern match not just on where the player is, but on the 2 steps in front of them. So I need to exhaustively pattern match on 3 cells, 2 of which have a complex hierarchy of possible states they can be in. Thankfully, sealed interfaces and pattern-matching made this much easier. Here is a direct link to the path-finding algorithm itself ([4] and [5]). I'd specifically like to focus on [5]. Lines 501, 502, and 503 of [5] hold the first, second, and third cells mentioned in the paragraph above. And then 506 onwards is the exhaustive pattern match on all possible states for those 3 cells. I nested switch expressions and used record patterns to handle the branching logic efficiently. It ended up being a >60 line switch expression. Trying to do this with if statements instead would have been prohibitively verbose. In fact, for fun, I tried to recreate this same logic in a couple of different ways. Here is the result [6], and compare it to [7]. I don't know what you think, but for me, if I had to write code like [6], I would switch to a different language. I know because I actually tried a very similar project to this in Java, and I had to put it down because I couldn't fit all of the info in my head. Funnily enough, I had the idea for this HelltakerPathFinder back before I knew what pattern matching in Java was. I remember thinking to myself that trying to solve this problem in Java would have been too painful and difficult, and that I should probably use Haskell or JavaScript instead. This segues very nicely into my next point. Not only did switch patterns and record patterns allow me to write code that would have been too difficult for me previously, it also freed up my mind to focus on significantly harder problems. I set a standard for myself when making this project -- I wanted my algorithm to "beat" the entire game in under 60 seconds. However, early on in the project, I ran into several performance issues. The first level alone was taking minutes to calculate instead of seconds. Because my code was so simple and easy to follow along, I was able to use process of elimination and identify the only possible places that inefficiencies could exist. More specifically, because most of my code was so simple and straightforward to follow, I quickly realized that the only problem was that my algorithm was not being intelligent enough. Because of that, I was able to make a focused effort on a bunch of different optimization strategies, such as prioritizing directions by likeliness to succeed, distance to the goal, obstacles in the way, etc. None of those strategies ended up bearing any fruit, but they led me to the real solution - short circuiting. So then, I tried to short circuit to failure whenever the player was too far from the goal ("as the crow flies") [9]. That gave a significant boost in performance, such that my first level completed in 20 seconds. However, most of the other levels still took over 60 seconds to run each. I needed all 9 levels COMBINED to run under 60 seconds, so again, not good enough. After more struggling, I realized that I needed to keep a history of the paths I was taking in order to more intelligently short circuit. I tried to save my steps and then tried failing if I retread a cell on the grid, but that didn't work because some puzzles actually require you to retread your steps And then I got hit with a flash of brilliance -- if I want to track my steps across the board, I should keep track of the state of the entire board. Meaning, I decided to create a java.util.Map where my key was the entire state of the board for my current attempt (including player location), and the value was the number of steps left on my current attempt. My recursive algorithm was already using immutable copies of the board, so I just passed in a mutable Map, used Map::merge to compare the current path I was on to what was contained within my map, and if the map had an entry, I checked to see if the number of steps left was greater than what was recorded on the map. If the map's value was smaller, I could safely assume that the path I was taking could be short circuited, and I could stop traveling down this branch because all paths from it were guaranteed to be inefficient. Here is the strategy -- [10] and [11]. I remember laughing to myself incredulously when I first thought of it. After all, Map keys should be small and simple, like Strings, or tiny data aggregations. Not a 2 dimensional List of a very complex data type. And worse yet, my value was a singular Integer. This entire structure was backwards and off. When I tried running it, I thought "Surely this is a terrible idea that shouldn't work." Immediately, most of my computation times dropped to less than 1 second. And the most difficult level of the bunch (Chapter 9) only took 15 seconds to complete. Not only was I able to beat the entire game in under 60 seconds, I was able to beat it in under 23 seconds -- less than half the time I had aimed for. SUCCESS In conclusion, these new Amber features have helped me to solve problems that were previously out of reach. I have tried many times to solve this exact type of problem, but I previously wasn't able to because I was too busy trying to manage things like exhaustiveness and maintainability in my head while I was also trying to optimize the code. Since switch patterns and record patterns allowed me to dissolve away most of the needless complexity, I was able to solve problems I previously would have used other languages for. And even if I had used Haskell or JavaScript, I never would have been able to come up with that short circuiting strategy of carrying the entirety of my board state as keys of a map lol. And with the exception of the data conversion point mentioned very early on, there was really no negatives or downside -- Amber features just felt like a straight improvement of what I was doing before. Thank you all for your time! David Alayachew [1]=https://github.com/davidalayachew/HelltakerPathFinder#readme [2]=https://mail.openjdk.org/pipermail/amber-dev/2022-September/007456.html [3]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Cell.java [4]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Main.java#L69 [5]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Board.java#L487 [6]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/potential_alternatives.txt [7]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Board.java#L505 [8]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Main.java#L112 [9]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Main.java#L95 [10]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Main.java#L102 [11]= https://github.com/davidalayachew/HelltakerPathFinder/blob/main/Helltaker/Main.java#L196 -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Apr 26 14:36:14 2023 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 26 Apr 2023 10:36:14 -0400 Subject: My experience building a path-finding algorithm using Amber In-Reply-To: References: Message-ID: <20836034-2d36-63fa-56be-71b4759c840c@oracle.com> Thanks for the nice success story and the kind words.? A few comments: > Since all elements of this structure were either sealed, a record, or > an enum, that meant that my domain of possible types (and thus, > solutions) was very bounded. That boundedness made reasoning about my > domain much easier. This is the power of "make illegal states unrepresentable".? By using the type system to represent only the valid states of your domain, code can focus on the happy path. I want to dig a little into the one "still could be improved" comment: > * Converting untyped data into typed data still feels frustrating and > difficult. I was able to bypass most of it this time around because, > thankfully, this project had very little conversion (whereas my > previous project [2] was completely built around that concept) Indeed, there is more to go here.? The good news is that this challenge is a big part of why we embarked on the pattern matching story in the first place.? We've gotten far enough on this story that it has enabled some of the things you talk about, but we still have a ways to go. Untyped data is a fact of life; Java is not a bubble, and we will always be dealing with unstructured user input, untyped documents like JSON, or typed data from another type system, such as SQL result sets or XML which rigorously uses schema to enforce types.? Even if an XSD tells us that a given element or attribute is of type `xsd:integer`, that doesn't mean "signed 32 bit int".? And as program units get "smaller" (break up them monoliths), all the code gets "closer to the boundary", and at the boundary, things are untyped. There are several approaches to untyped data taken by Java programs, all of which are pretty limited: ?- Deal with it directly as untyped data.? This makes our Java programs stringly typed. ?- Try to map it automatically via ORMs, JAXB, and similar data binding mechanisms.? This is practical for semi-typed data, but often runs out of gas before we're done. ?- Painstakingly unpack it into Java data using imperative tools like JSON-P.? This typically results in large masses of error-prone and brittle code (Does it have a key called X?? Is that key mapped to an integer?? Is that integer within range of `int`?).? It is not uncommon to see 50-100 lines of awful "unpacking" code followed by a few lines of clean "business logic" and then some more "repacking code".? This often makes me wonder "Where's the Beef?" To deal with untyped data, we need an organized way to define the boundary.? To avoid descending into a rat's nest of horrible imperative checks like the third solution, our boundary management tools need to *compose*. Pattern matching composes.? If I'm trying to match `"x": "3"` to extract the int value, I need a way to express "key X", "maps to double", "double can be converted to int" in a single go, not treat it as three separate sources of error, and ignore the parts of the structure I am not interested in.? The key thing the language was missing to get there was a flexible, composable, fused test-and-conditionally-extract operation. More to come! -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Wed Apr 26 17:39:26 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Wed, 26 Apr 2023 13:39:26 -0400 Subject: My experience building a path-finding algorithm using Amber In-Reply-To: <20836034-2d36-63fa-56be-71b4759c840c@oracle.com> References: <20836034-2d36-63fa-56be-71b4759c840c@oracle.com> Message-ID: Hello Brian, Thank you for the response! > Thanks for the nice success story and the kind words. A > few comments: > > > Since all elements of this structure were either > > sealed, a record, or an enum, that meant that my > > domain of possible types (and thus, solutions) was very > > bounded. That boundedness made reasoning about my > > domain much easier. > > This is the power of "make illegal states > unrepresentable". By using the type system to represent > only the valid states of your domain, code can focus on > the happy path. I see what you mean now. Yes, now that I think about it, exception cases and trying to keep all the logic in my head was really me trying to juggle too many unhappy paths, bogging down my development efforts until I am just spinning my wheels. Having the weight off of my shoulders allows me to move and think more freely and creatively. To my understanding, the ability to "make illegal states unrepresentable" comes from the fact that Java now has Abstract Data Types. That makes ADT's very powerful and something I should start defaulting to when the situation allows it to, so that I can get all of this freedom and ease of movement. As for the rest of your response, I'll just address it all in total without quoting. Yes, I definitely agree. Without pattern-matching/Data-Oriented-Programming/ADT's, even the pain points I mentioned would be far worse. In fact, prior to using Amber features, I was in the camp of using solutions 1 and 2 exclusively, since 3 was just too complex and error prone to maintain in the face of refactoring or changing requirements. It's only because of the thus far released features that 3 even became an option for me at all. I am very excited for what this future with pattern-matching looks like. In my previous attempt [1], I went down the painstaking route of getting my data into fully strongly typed objects, so I paid the full price of translation, as mentioned in the link. And I did exactly like you said in my previous attempt -- I ended up with this >60 line mess of trying to turn untyped data into typed data [2]. Can you give me a tiny snippet of what that future might look like? You mentioned composing, and that makes a lot of sense, but I was curious about what the things we are composing look like. You say we are composing pattern matching. You mentioned matching the String in your example, `"x":"3"`. I used an ugly java.util.regex.Pattern to handle a String of a similar format in my previous attempt [3]. What might the future look like when pattern matching that `"x":"3"`? I see that java.util.regex.Pattern introduces sort of a second class citizen version of pattern matching right now. You use Pattern::matcher to test for a match. And the only way to extract the successful match is to call the Matcher::matches method first, then extract. Java will return a failure if you do not call this first, allowing essentially the same functionality that pattern matching gives you. Are we going to see a more condensed and first class version of this when turning Stringly-typed data into strongly typed data? And if so, what's your guess of what that would look like? Thank you for your time and insight! David Alayachew [1]=https://mail.openjdk.org/pipermail/amber-dev/2022-September/007456.html [2]= https://github.com/davidalayachew/RulesEngine/blob/main/src/main/java/io/github/davidalayachew/ClassParser.java#L22 [3]= https://github.com/davidalayachew/RulesEngine/blob/main/src/main/java/io/github/davidalayachew/IsIdentifierAType.java#L9 -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Apr 29 03:55:22 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 28 Apr 2023 23:55:22 -0400 Subject: Question about testing preview features Message-ID: Hello, I saw that build 20 of JDK 21 was just released [1]. In it, there are the Sequenced Collections changes, some Loom changes, and more. I want to understand the following. * Where do we go to see when a feature has been merged into their respective repository (for example, Sequenced Collections into openjdk/amber)? The JDK Bug System? * Is there a gap between when a feature is merged into the respective repository vs the openjdk/jdk? * Where do we go to see when a feature has been merged into the openjdk/jdk? The reason I ask all of these questions is because I recently started building Amber, Loom, and Valhalla from source. It is not easy, and I see failed tests when I finish, so I feel like I am doing it wrong. And more importantly, it takes a very long time, even building all 3 concurrently, to get the newest changes on my system. In short, if the latest build of the openjdk/jdk is going to contain all the newest features, then I am not sure that spending 4 hours of my day waiting for 3 repos to build makes sense compared to just spending 10 minutes downloading just the 1, unpacking it, then pointing my variables to it. So, I guess a final question would be, what benefit is there in building from source? I want to test these features and contribute, so is there some form of contribution that I am able to provide building from source that I would not be able to provide if I just downloaded the latest build and started using it? Thank you all for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Sat Apr 29 03:56:25 2023 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 28 Apr 2023 23:56:25 -0400 Subject: Question about testing preview features In-Reply-To: References: Message-ID: Whoops, forgot the link to build 20 of JDK 21. Here it is. [1]=https://jdk.java.net/21/ -------------- next part -------------- An HTML attachment was scrubbed... URL: