From davidalayachew at gmail.com Mon Jan 1 06:26:25 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Jan 2024 01:26:25 -0500 Subject: New method on java.util.function.Function -- ternary method In-Reply-To: References: Message-ID: Hello all, After reading through Brian Goetz's "Effect cases on Switch" (specifically, the "Other switch tricks" section), I have decided to not implement this feature after all. The fact is, switch expressions, especially with "when-clauses", does a better job of clarifying intent than this feature does. Sure, there is slightly more verbosity, but the intent of this feature was never to minimize the character count, it was to increase clarity. And as is, switch expressions with when clauses accomplish that better. Here is a link to the subsection of the abovementioned article -- https://inside.java/2023/12/15/switch-case-effect/#other-switch-tricks Could someone help me close this on JBS? Or should it just not be closed at all? Here is a link to the JBS -- https://bugs.openjdk.org/browse/JDK-8319802 Thank you for your time and help! David Alayachew On Thu, Nov 9, 2023 at 1:19?AM David Alayachew wrote: > Oh, I will also add a specialized version of this method to > java.util.function.UnaryOperator. That is because UnaryOperator is a > subinterface of Function. Function goes T -> R, but UnaryOperator just goes > T -> T. We do not want UnaryOperator to have the T -> R static function > included due to inheritance, so it will get its own version that has the > same method name and parameters, but the type parameters will be different. > > Here is another mockup - this time for UnaryOperator2's version of the > same method. > > > interface UnaryOperator2 extends Function2, UnaryOperator > > { > > > > public static UnaryOperator2 ternaryApply > > ( > > Predicate test, > > Function trueFunction, > > Function falseFunction > > ) > > { > > > > return > > (T input) -> > > test.test(input) > > ? trueFunction.apply(input) > > : falseFunction.apply(input) > > ; > > > > } > > > > } > > On Thu, Nov 9, 2023 at 12:12?AM David Alayachew > wrote: > >> It has been a month since I sent this proposal. Since no one has told me >> that this is a terrible idea, I will submit this as an enhancement to JBS, >> and once the ticket is made, start work on creating a pull request. >> >> On Tue, Oct 3, 2023 at 3:13?AM David Alayachew >> wrote: >> >>> Whoops, bad import. >>> >>> Please replace the following line with the one after it. >>> >>> > import java.util.function.Function; >>> >>> > import java.util.function.*; >>> >>> On Tue, Oct 3, 2023 at 3:09?AM David Alayachew >>> wrote: >>> >>>> Hello all, >>>> >>>> I have an idea that I want to run by you all -- a new method on >>>> java.util.function.Function to capture ternary operations. >>>> >>>> Here is a mockup of what I wanted to do. >>>> >>>> > >>>> > import java.util.function.Function; >>>> > >>>> > @FunctionalInterface >>>> > public interface Function2 extends Function >>>> > { >>>> > >>>> > public static Function ternary >>>> > ( >>>> > Predicate test, >>>> > Function trueOutput, >>>> > Function falseOutput >>>> > ) >>>> > { >>>> > >>>> > return >>>> > (I input) -> >>>> > test.test(input) >>>> > ? trueOutput.apply(input) >>>> > : falseOutput.apply(input) >>>> > ; >>>> > >>>> > } >>>> > >>>> > } >>>> > >>>> >>>> I think this is useful for a few reasons. >>>> >>>> * This composes, just like the ternary operator itself. >>>> >>>> * It pairs well with Function.identity() and method references to >>>> clearly (but concisely) communicate intent. >>>> >>>> * Ternary operations are common, so this will find great use by >>>> developers of all sorts. >>>> >>>> There is at least one part I don't quite like about this design - what >>>> if one (or both!) of your outputs is not a functional transformation of the >>>> input? >>>> >>>> For example, String username = id.isBlank() ? "UNKNOWN" : lookup(id); >>>> >>>> Obviously, this is easy to work around - simply ignore the input of the >>>> function. But you lose clarity and simplicity that way. I would put a note >>>> in the javadoc that says that this method should only be used in instances >>>> where both outputs are a functional transformation of the input. That way, >>>> intent is clarified. But if we go that route, maybe this function should >>>> get a better name to capture that? testThenApply? ternaryTransform? >>>> ternaryApply? >>>> >>>> Thank you for your time and help! >>>> David Alayachew >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 1 06:29:42 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Jan 2024 01:29:42 -0500 Subject: New method on java.util.function.Function -- ternary method In-Reply-To: References: Message-ID: On a side note, I spy a formatting error in the first code block. Looks like the backticks shouldn't be there, and I think the phrase following them should be outside of the code block. On Mon, Jan 1, 2024 at 1:26?AM David Alayachew wrote: > Hello all, > > After reading through Brian Goetz's "Effect cases on Switch" > (specifically, the "Other switch tricks" section), I have decided to not > implement this feature after all. The fact is, switch expressions, > especially with "when-clauses", does a better job of clarifying intent than > this feature does. Sure, there is slightly more verbosity, but the intent > of this feature was never to minimize the character count, it was to > increase clarity. And as is, switch expressions with when clauses > accomplish that better. > > Here is a link to the subsection of the abovementioned article -- > https://inside.java/2023/12/15/switch-case-effect/#other-switch-tricks > > Could someone help me close this on JBS? Or should it just not be closed > at all? > > Here is a link to the JBS -- https://bugs.openjdk.org/browse/JDK-8319802 > > Thank you for your time and help! > David Alayachew > > On Thu, Nov 9, 2023 at 1:19?AM David Alayachew > wrote: > >> Oh, I will also add a specialized version of this method to >> java.util.function.UnaryOperator. That is because UnaryOperator is a >> subinterface of Function. Function goes T -> R, but UnaryOperator just goes >> T -> T. We do not want UnaryOperator to have the T -> R static function >> included due to inheritance, so it will get its own version that has the >> same method name and parameters, but the type parameters will be different. >> >> Here is another mockup - this time for UnaryOperator2's version of the >> same method. >> >> > interface UnaryOperator2 extends Function2, UnaryOperator >> > { >> > >> > public static UnaryOperator2 ternaryApply >> > ( >> > Predicate test, >> > Function trueFunction, >> > Function falseFunction >> > ) >> > { >> > >> > return >> > (T input) -> >> > test.test(input) >> > ? trueFunction.apply(input) >> > : falseFunction.apply(input) >> > ; >> > >> > } >> > >> > } >> >> On Thu, Nov 9, 2023 at 12:12?AM David Alayachew >> wrote: >> >>> It has been a month since I sent this proposal. Since no one has told me >>> that this is a terrible idea, I will submit this as an enhancement to JBS, >>> and once the ticket is made, start work on creating a pull request. >>> >>> On Tue, Oct 3, 2023 at 3:13?AM David Alayachew >>> wrote: >>> >>>> Whoops, bad import. >>>> >>>> Please replace the following line with the one after it. >>>> >>>> > import java.util.function.Function; >>>> >>>> > import java.util.function.*; >>>> >>>> On Tue, Oct 3, 2023 at 3:09?AM David Alayachew < >>>> davidalayachew at gmail.com> wrote: >>>> >>>>> Hello all, >>>>> >>>>> I have an idea that I want to run by you all -- a new method on >>>>> java.util.function.Function to capture ternary operations. >>>>> >>>>> Here is a mockup of what I wanted to do. >>>>> >>>>> > >>>>> > import java.util.function.Function; >>>>> > >>>>> > @FunctionalInterface >>>>> > public interface Function2 extends Function >>>>> > { >>>>> > >>>>> > public static Function ternary >>>>> > ( >>>>> > Predicate test, >>>>> > Function trueOutput, >>>>> > Function falseOutput >>>>> > ) >>>>> > { >>>>> > >>>>> > return >>>>> > (I input) -> >>>>> > test.test(input) >>>>> > ? trueOutput.apply(input) >>>>> > : falseOutput.apply(input) >>>>> > ; >>>>> > >>>>> > } >>>>> > >>>>> > } >>>>> > >>>>> >>>>> I think this is useful for a few reasons. >>>>> >>>>> * This composes, just like the ternary operator itself. >>>>> >>>>> * It pairs well with Function.identity() and method references to >>>>> clearly (but concisely) communicate intent. >>>>> >>>>> * Ternary operations are common, so this will find great use by >>>>> developers of all sorts. >>>>> >>>>> There is at least one part I don't quite like about this design - what >>>>> if one (or both!) of your outputs is not a functional transformation of the >>>>> input? >>>>> >>>>> For example, String username = id.isBlank() ? "UNKNOWN" : lookup(id); >>>>> >>>>> Obviously, this is easy to work around - simply ignore the input of >>>>> the function. But you lose clarity and simplicity that way. I would put a >>>>> note in the javadoc that says that this method should only be used in >>>>> instances where both outputs are a functional transformation of the input. >>>>> That way, intent is clarified. But if we go that route, maybe this function >>>>> should get a better name to capture that? testThenApply? ternaryTransform? >>>>> ternaryApply? >>>>> >>>>> Thank you for your time and help! >>>>> David Alayachew >>>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 1 06:36:44 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Jan 2024 01:36:44 -0500 Subject: New method on java.util.function.Function -- ternary method In-Reply-To: References: Message-ID: Here is a direct link to that formatting error. Same link as above. https://bugs.openjdk.org/browse/JDK-8319802 On Mon, Jan 1, 2024 at 1:29?AM David Alayachew wrote: > On a side note, I spy a formatting error in the first code block. Looks > like the backticks shouldn't be there, and I think the phrase following > them should be outside of the code block. > > On Mon, Jan 1, 2024 at 1:26?AM David Alayachew > wrote: > >> Hello all, >> >> After reading through Brian Goetz's "Effect cases on Switch" >> (specifically, the "Other switch tricks" section), I have decided to not >> implement this feature after all. The fact is, switch expressions, >> especially with "when-clauses", does a better job of clarifying intent than >> this feature does. Sure, there is slightly more verbosity, but the intent >> of this feature was never to minimize the character count, it was to >> increase clarity. And as is, switch expressions with when clauses >> accomplish that better. >> >> Here is a link to the subsection of the abovementioned article -- >> https://inside.java/2023/12/15/switch-case-effect/#other-switch-tricks >> >> Could someone help me close this on JBS? Or should it just not be closed >> at all? >> >> Here is a link to the JBS -- https://bugs.openjdk.org/browse/JDK-8319802 >> >> Thank you for your time and help! >> David Alayachew >> >> On Thu, Nov 9, 2023 at 1:19?AM David Alayachew >> wrote: >> >>> Oh, I will also add a specialized version of this method to >>> java.util.function.UnaryOperator. That is because UnaryOperator is a >>> subinterface of Function. Function goes T -> R, but UnaryOperator just goes >>> T -> T. We do not want UnaryOperator to have the T -> R static function >>> included due to inheritance, so it will get its own version that has the >>> same method name and parameters, but the type parameters will be different. >>> >>> Here is another mockup - this time for UnaryOperator2's version of the >>> same method. >>> >>> > interface UnaryOperator2 extends Function2, UnaryOperator >>> > { >>> > >>> > public static UnaryOperator2 ternaryApply >>> > ( >>> > Predicate test, >>> > Function trueFunction, >>> > Function falseFunction >>> > ) >>> > { >>> > >>> > return >>> > (T input) -> >>> > test.test(input) >>> > ? trueFunction.apply(input) >>> > : falseFunction.apply(input) >>> > ; >>> > >>> > } >>> > >>> > } >>> >>> On Thu, Nov 9, 2023 at 12:12?AM David Alayachew < >>> davidalayachew at gmail.com> wrote: >>> >>>> It has been a month since I sent this proposal. Since no one has told >>>> me that this is a terrible idea, I will submit this as an enhancement to >>>> JBS, and once the ticket is made, start work on creating a pull request. >>>> >>>> On Tue, Oct 3, 2023 at 3:13?AM David Alayachew < >>>> davidalayachew at gmail.com> wrote: >>>> >>>>> Whoops, bad import. >>>>> >>>>> Please replace the following line with the one after it. >>>>> >>>>> > import java.util.function.Function; >>>>> >>>>> > import java.util.function.*; >>>>> >>>>> On Tue, Oct 3, 2023 at 3:09?AM David Alayachew < >>>>> davidalayachew at gmail.com> wrote: >>>>> >>>>>> Hello all, >>>>>> >>>>>> I have an idea that I want to run by you all -- a new method on >>>>>> java.util.function.Function to capture ternary operations. >>>>>> >>>>>> Here is a mockup of what I wanted to do. >>>>>> >>>>>> > >>>>>> > import java.util.function.Function; >>>>>> > >>>>>> > @FunctionalInterface >>>>>> > public interface Function2 extends Function >>>>>> > { >>>>>> > >>>>>> > public static Function ternary >>>>>> > ( >>>>>> > Predicate test, >>>>>> > Function trueOutput, >>>>>> > Function falseOutput >>>>>> > ) >>>>>> > { >>>>>> > >>>>>> > return >>>>>> > (I input) -> >>>>>> > test.test(input) >>>>>> > ? trueOutput.apply(input) >>>>>> > : falseOutput.apply(input) >>>>>> > ; >>>>>> > >>>>>> > } >>>>>> > >>>>>> > } >>>>>> > >>>>>> >>>>>> I think this is useful for a few reasons. >>>>>> >>>>>> * This composes, just like the ternary operator itself. >>>>>> >>>>>> * It pairs well with Function.identity() and method references to >>>>>> clearly (but concisely) communicate intent. >>>>>> >>>>>> * Ternary operations are common, so this will find great use by >>>>>> developers of all sorts. >>>>>> >>>>>> There is at least one part I don't quite like about this design - >>>>>> what if one (or both!) of your outputs is not a functional transformation >>>>>> of the input? >>>>>> >>>>>> For example, String username = id.isBlank() ? "UNKNOWN" : lookup(id); >>>>>> >>>>>> Obviously, this is easy to work around - simply ignore the input of >>>>>> the function. But you lose clarity and simplicity that way. I would put a >>>>>> note in the javadoc that says that this method should only be used in >>>>>> instances where both outputs are a functional transformation of the input. >>>>>> That way, intent is clarified. But if we go that route, maybe this function >>>>>> should get a better name to capture that? testThenApply? ternaryTransform? >>>>>> ternaryApply? >>>>>> >>>>>> Thank you for your time and help! >>>>>> David Alayachew >>>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 1 06:37:04 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Jan 2024 01:37:04 -0500 Subject: New method on java.util.function.Function -- ternary method In-Reply-To: References: Message-ID: Sorry, this one. https://inside.java/2023/12/15/switch-case-effect/#other-switch-tricks On Mon, Jan 1, 2024 at 1:36?AM David Alayachew wrote: > Here is a direct link to that formatting error. Same link as above. > https://bugs.openjdk.org/browse/JDK-8319802 > > On Mon, Jan 1, 2024 at 1:29?AM David Alayachew > wrote: > >> On a side note, I spy a formatting error in the first code block. Looks >> like the backticks shouldn't be there, and I think the phrase following >> them should be outside of the code block. >> >> On Mon, Jan 1, 2024 at 1:26?AM David Alayachew >> wrote: >> >>> Hello all, >>> >>> After reading through Brian Goetz's "Effect cases on Switch" >>> (specifically, the "Other switch tricks" section), I have decided to not >>> implement this feature after all. The fact is, switch expressions, >>> especially with "when-clauses", does a better job of clarifying intent than >>> this feature does. Sure, there is slightly more verbosity, but the intent >>> of this feature was never to minimize the character count, it was to >>> increase clarity. And as is, switch expressions with when clauses >>> accomplish that better. >>> >>> Here is a link to the subsection of the abovementioned article -- >>> https://inside.java/2023/12/15/switch-case-effect/#other-switch-tricks >>> >>> Could someone help me close this on JBS? Or should it just not be closed >>> at all? >>> >>> Here is a link to the JBS -- https://bugs.openjdk.org/browse/JDK-8319802 >>> >>> Thank you for your time and help! >>> David Alayachew >>> >>> On Thu, Nov 9, 2023 at 1:19?AM David Alayachew >>> wrote: >>> >>>> Oh, I will also add a specialized version of this method to >>>> java.util.function.UnaryOperator. That is because UnaryOperator is a >>>> subinterface of Function. Function goes T -> R, but UnaryOperator just goes >>>> T -> T. We do not want UnaryOperator to have the T -> R static function >>>> included due to inheritance, so it will get its own version that has the >>>> same method name and parameters, but the type parameters will be different. >>>> >>>> Here is another mockup - this time for UnaryOperator2's version of the >>>> same method. >>>> >>>> > interface UnaryOperator2 extends Function2, UnaryOperator >>>> > { >>>> > >>>> > public static UnaryOperator2 ternaryApply >>>> > ( >>>> > Predicate test, >>>> > Function trueFunction, >>>> > Function falseFunction >>>> > ) >>>> > { >>>> > >>>> > return >>>> > (T input) -> >>>> > test.test(input) >>>> > ? trueFunction.apply(input) >>>> > : falseFunction.apply(input) >>>> > ; >>>> > >>>> > } >>>> > >>>> > } >>>> >>>> On Thu, Nov 9, 2023 at 12:12?AM David Alayachew < >>>> davidalayachew at gmail.com> wrote: >>>> >>>>> It has been a month since I sent this proposal. Since no one has told >>>>> me that this is a terrible idea, I will submit this as an enhancement to >>>>> JBS, and once the ticket is made, start work on creating a pull request. >>>>> >>>>> On Tue, Oct 3, 2023 at 3:13?AM David Alayachew < >>>>> davidalayachew at gmail.com> wrote: >>>>> >>>>>> Whoops, bad import. >>>>>> >>>>>> Please replace the following line with the one after it. >>>>>> >>>>>> > import java.util.function.Function; >>>>>> >>>>>> > import java.util.function.*; >>>>>> >>>>>> On Tue, Oct 3, 2023 at 3:09?AM David Alayachew < >>>>>> davidalayachew at gmail.com> wrote: >>>>>> >>>>>>> Hello all, >>>>>>> >>>>>>> I have an idea that I want to run by you all -- a new method on >>>>>>> java.util.function.Function to capture ternary operations. >>>>>>> >>>>>>> Here is a mockup of what I wanted to do. >>>>>>> >>>>>>> > >>>>>>> > import java.util.function.Function; >>>>>>> > >>>>>>> > @FunctionalInterface >>>>>>> > public interface Function2 extends Function >>>>>>> > { >>>>>>> > >>>>>>> > public static Function ternary >>>>>>> > ( >>>>>>> > Predicate test, >>>>>>> > Function trueOutput, >>>>>>> > Function falseOutput >>>>>>> > ) >>>>>>> > { >>>>>>> > >>>>>>> > return >>>>>>> > (I input) -> >>>>>>> > test.test(input) >>>>>>> > ? trueOutput.apply(input) >>>>>>> > : falseOutput.apply(input) >>>>>>> > ; >>>>>>> > >>>>>>> > } >>>>>>> > >>>>>>> > } >>>>>>> > >>>>>>> >>>>>>> I think this is useful for a few reasons. >>>>>>> >>>>>>> * This composes, just like the ternary operator itself. >>>>>>> >>>>>>> * It pairs well with Function.identity() and method references to >>>>>>> clearly (but concisely) communicate intent. >>>>>>> >>>>>>> * Ternary operations are common, so this will find great use by >>>>>>> developers of all sorts. >>>>>>> >>>>>>> There is at least one part I don't quite like about this design - >>>>>>> what if one (or both!) of your outputs is not a functional transformation >>>>>>> of the input? >>>>>>> >>>>>>> For example, String username = id.isBlank() ? "UNKNOWN" : lookup(id); >>>>>>> >>>>>>> Obviously, this is easy to work around - simply ignore the input of >>>>>>> the function. But you lose clarity and simplicity that way. I would put a >>>>>>> note in the javadoc that says that this method should only be used in >>>>>>> instances where both outputs are a functional transformation of the input. >>>>>>> That way, intent is clarified. But if we go that route, maybe this function >>>>>>> should get a better name to capture that? testThenApply? ternaryTransform? >>>>>>> ternaryApply? >>>>>>> >>>>>>> Thank you for your time and help! >>>>>>> David Alayachew >>>>>>> >>>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 1 07:11:54 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Jan 2024 02:11:54 -0500 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? Message-ID: Hello Amber Dev Team, I read Brian Goetz's "Case Effect on Switch" (looks like it recently got renamed to "Uniform handling of failure in switch"), and I have a quick question. I think it's cool to bring error handling into switch, but will we still get the exhaustiveness/totality on checked exceptions that we are used to on the try catch side? I feel like it would be weird and unintuitive if it didn't, since switch expressions already give us exhaustiveness/totality, and that's basically the biggest reason to use a switch expression in the first place. Leaving it out for exceptions would just feel mean. Plus, having this feature would really help with clarifying intent and with readability. Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 1 07:14:34 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Jan 2024 02:14:34 -0500 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? In-Reply-To: References: Message-ID: Forgot to link the article -- https://inside.java/2023/12/15/switch-case-effect/ Also, if the article is already confirming this feature, I definitely am not seeing it. There is one bit where Brian uses the word "totalize", which I suppose might be confirmation, but I would far prefer that that is spelled out explicitly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From holo3146 at gmail.com Mon Jan 1 07:26:57 2024 From: holo3146 at gmail.com (Holo The Sage Wolf) Date: Mon, 1 Jan 2024 09:26:57 +0200 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? In-Reply-To: References: Message-ID: Checked exceptions will remain totally checked. Given a function f, let checkF be the set of checked exceptions, then the expression: switch(root(args)) { case branch0 -> do0(args); case branch1 -> do1(args); ... case branch1 -> doN(args); case throws fBranch0 -> handle0(args); case throws fBranch0 -> handle1(args); ... case throws fBranch0 -> handleM(args); } Will have the following set of checked exceptions: (checkRoot - {fBranch0, ... fBranchM}) + checkDo0 + ... + checkDoN + checkHandle0 + ... + checkHandleM Where minus is set difference and plus is union On Mon, 1 Jan 2024, 09:12 David Alayachew, wrote: > Hello Amber Dev Team, > > I read Brian Goetz's "Case Effect on Switch" (looks like it recently got > renamed to "Uniform handling of failure in switch"), and I have a quick > question. > > I think it's cool to bring error handling into switch, but will we still > get the exhaustiveness/totality on checked exceptions that we are used to > on the try catch side? > > I feel like it would be weird and unintuitive if it didn't, since switch > expressions already give us exhaustiveness/totality, and that's basically > the biggest reason to use a switch expression in the first place. Leaving > it out for exceptions would just feel mean. Plus, having this feature would > really help with clarifying intent and with readability. > > Thank you for your time and help! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 1 07:37:03 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Jan 2024 02:37:03 -0500 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? In-Reply-To: References: Message-ID: Hello, Thank you for your response! Yes, this makes a lot of sense, and I figured it would be the case. But the article (surprisingly) didn't address that point at all. Likely because just broaching the topic of exceptions in switch was a task on its own, and this subject will come later. I also appreciate the set theory formula at the end. You also posted that in the other thread, and I still plan to respond to that once I get the chance. Thank you for your time and help! David Alayachew On Mon, Jan 1, 2024 at 2:27?AM Holo The Sage Wolf wrote: > Checked exceptions will remain totally checked. > > Given a function f, let checkF be the set of checked exceptions, then the > expression: > > switch(root(args)) { > case branch0 -> do0(args); > case branch1 -> do1(args); > ... > case branch1 -> doN(args); > case throws fBranch0 -> handle0(args); > case throws fBranch0 -> handle1(args); > ... > case throws fBranch0 -> handleM(args); > } > > Will have the following set of checked exceptions: > > (checkRoot - {fBranch0, ... fBranchM}) + checkDo0 + ... + checkDoN + > checkHandle0 + ... + checkHandleM > > Where minus is set difference and plus is union > > On Mon, 1 Jan 2024, 09:12 David Alayachew, > wrote: > >> Hello Amber Dev Team, >> >> I read Brian Goetz's "Case Effect on Switch" (looks like it recently got >> renamed to "Uniform handling of failure in switch"), and I have a quick >> question. >> >> I think it's cool to bring error handling into switch, but will we still >> get the exhaustiveness/totality on checked exceptions that we are used to >> on the try catch side? >> >> I feel like it would be weird and unintuitive if it didn't, since switch >> expressions already give us exhaustiveness/totality, and that's basically >> the biggest reason to use a switch expression in the first place. Leaving >> it out for exceptions would just feel mean. Plus, having this feature would >> really help with clarifying intent and with readability. >> >> Thank you for your time and help! >> David Alayachew >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Thu Jan 4 11:01:58 2024 From: duke at openjdk.org (duke) Date: Thu, 4 Jan 2024 11:01:58 GMT Subject: git: openjdk/amber-docs: 2 new changesets Message-ID: <4ed68631-8adb-4405-8f5a-a04a44014ae2@openjdk.org> Changeset: 3caf0eb9 Author: Gavin Bierman Date: 2024-01-04 10:53:59 +0000 URL: https://git.openjdk.org/amber-docs/commit/3caf0eb9f2e5cc067652e72c1267acb82780a7ac Refresh ! site/_index.md Changeset: 5aad49cb Author: Gavin Bierman Date: 2024-01-04 10:59:32 +0000 URL: https://git.openjdk.org/amber-docs/commit/5aad49cb4160aaba981cd22488c37b5a4ac3c84d Merge branch 'master' of https://github.com/openjdk/amber-docs ! site/_index.md ! site/_index.md From brian.goetz at oracle.com Thu Jan 4 18:59:48 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 4 Jan 2024 13:59:48 -0500 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? In-Reply-To: References: Message-ID: <789a1353-0827-40e1-8b92-26b9d2556a6a@oracle.com> There's another piece of the answer here as well, which is the existing boundaries for type-checking checked exceptions.? And that existing boundary is solely method boundaries.? (Lambdas are methods.) A method can be declared to throw a set of checked exceptions; the only place we check for "you can't throw X here" is at the top level of a method.? (What exceptions might be thrown from the top level may bubble up from more deeply nested blocks within a top-level statement, through flow analysis.) The current proposal does not propose to change that; we are not making switches a boundary where we do method-style exception checking (we'd have to put a `throws` clause on a switch if we did.) On 1/1/2024 2:37 AM, David Alayachew wrote: > Hello, > > Thank you for your response! > > Yes, this makes a lot of sense, and I figured it would be the case. > But the article (surprisingly) didn't address that point at all. > Likely because just broaching the topic of exceptions in switch was a > task on its own, and this subject will come later. > > I also appreciate the set theory formula at the end. You also posted > that in the other thread, and I still plan to respond to that once I > get the chance. > > Thank you for your time and help! > David Alayachew > > On Mon, Jan 1, 2024 at 2:27?AM Holo The Sage Wolf > wrote: > > Checked exceptions will remain totally checked. > > Given a function f, let checkF be the set of checked exceptions, > then the expression: > > switch(root(args)) { > ? ? case branch0 -> do0(args); > ? ? case branch1 -> do1(args); > ? ? ... > ? ? case branch1 -> doN(args); > ? ? case throws fBranch0 -> handle0(args); > ? ? case throws fBranch0 -> handle1(args); > ? ? ... > ? ? case throws fBranch0 -> handleM(args); > } > > Will have the following set of checked exceptions: > > ? ?(checkRoot - {fBranch0, ... fBranchM}) + checkDo0 + ... + > checkDoN + checkHandle0 + ... + checkHandleM > > Where minus is set difference and plus is union > > On Mon, 1 Jan 2024, 09:12 David Alayachew, > wrote: > > Hello Amber Dev Team, > > I read Brian Goetz's "Case Effect on Switch" (looks like it > recently got renamed to "Uniform handling of failure in > switch"), and I have a quick question. > > I think it's cool to bring error handling into switch, but > will we still get the exhaustiveness/totality on checked > exceptions that we are used to on the try catch side? > > I feel like it would be weird and unintuitive if it didn't, > since switch expressions already give us > exhaustiveness/totality, and that's basically the biggest > reason to use a switch expression in the first place. Leaving > it out for exceptions would just feel mean. Plus, having this > feature would really help with clarifying intent and with > readability. > > Thank you for your time and help! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jan 5 01:34:10 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Thu, 4 Jan 2024 20:34:10 -0500 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? In-Reply-To: <789a1353-0827-40e1-8b92-26b9d2556a6a@oracle.com> References: <789a1353-0827-40e1-8b92-26b9d2556a6a@oracle.com> Message-ID: Hello Brian, Thank you for your response! Yet again, I did not think things through. I was only thinking of switches being inside of methods. But switches are expressions now, so they can be anywhere that we put an expression. So, I could put them on an instance field or a static field. How could we handle that if the switch wasn't total? Yes, what you say about method boundaries not only makes sense, but is practically required because switch is an expression. Thank you for your time and help! David Alayachew On Thu, Jan 4, 2024, 1:59 PM Brian Goetz wrote: > There's another piece of the answer here as well, which is the existing > boundaries for type-checking checked exceptions. And that existing > boundary is solely method boundaries. (Lambdas are methods.) > > A method can be declared to throw a set of checked exceptions; the only > place we check for "you can't throw X here" is at the top level of a > method. (What exceptions might be thrown from the top level may bubble up > from more deeply nested blocks within a top-level statement, through flow > analysis.) > > The current proposal does not propose to change that; we are not making > switches a boundary where we do method-style exception checking (we'd have > to put a `throws` clause on a switch if we did.) > > On 1/1/2024 2:37 AM, David Alayachew wrote: > > Hello, > > Thank you for your response! > > Yes, this makes a lot of sense, and I figured it would be the case. But > the article (surprisingly) didn't address that point at all. Likely because > just broaching the topic of exceptions in switch was a task on its own, and > this subject will come later. > > I also appreciate the set theory formula at the end. You also posted that > in the other thread, and I still plan to respond to that once I get the > chance. > > Thank you for your time and help! > David Alayachew > > On Mon, Jan 1, 2024 at 2:27?AM Holo The Sage Wolf > wrote: > >> Checked exceptions will remain totally checked. >> >> Given a function f, let checkF be the set of checked exceptions, then the >> expression: >> >> switch(root(args)) { >> case branch0 -> do0(args); >> case branch1 -> do1(args); >> ... >> case branch1 -> doN(args); >> case throws fBranch0 -> handle0(args); >> case throws fBranch0 -> handle1(args); >> ... >> case throws fBranch0 -> handleM(args); >> } >> >> Will have the following set of checked exceptions: >> >> (checkRoot - {fBranch0, ... fBranchM}) + checkDo0 + ... + checkDoN + >> checkHandle0 + ... + checkHandleM >> >> Where minus is set difference and plus is union >> >> On Mon, 1 Jan 2024, 09:12 David Alayachew, >> wrote: >> >>> Hello Amber Dev Team, >>> >>> I read Brian Goetz's "Case Effect on Switch" (looks like it recently got >>> renamed to "Uniform handling of failure in switch"), and I have a quick >>> question. >>> >>> I think it's cool to bring error handling into switch, but will we still >>> get the exhaustiveness/totality on checked exceptions that we are used to >>> on the try catch side? >>> >>> I feel like it would be weird and unintuitive if it didn't, since switch >>> expressions already give us exhaustiveness/totality, and that's basically >>> the biggest reason to use a switch expression in the first place. Leaving >>> it out for exceptions would just feel mean. Plus, having this feature would >>> really help with clarifying intent and with readability. >>> >>> Thank you for your time and help! >>> David Alayachew >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vab2048 at gmail.com Fri Jan 5 10:37:50 2024 From: vab2048 at gmail.com (Vikram Bakshi) Date: Fri, 5 Jan 2024 10:37:50 +0000 Subject: Allowing inheritance with Records? In-Reply-To: <32c477bc-3aeb-4be3-baf2-c67020b43f5a@oracle.com> References: <32c477bc-3aeb-4be3-baf2-c67020b43f5a@oracle.com> Message-ID: > Is there room for a more restricted category of abstract classes that records could extend without giving up their semantic benefits and safety guarantees? Quite possibly, including perhaps the notion of "abstract record", which permits layered modeling but maintains the safety guarantees of records? This is pretty much what I was thinking. An abstract record can extend either java.lang.record or another abstract record. The "non-abstract" record would then extend the abstract one. Just like how we do now for classes and abstract classes. > We explored abstract records during the design of records, and concluded that while they were a possibility, there are a number of details to work out, some of which are a little uncomfortable. > So there is room to explore this further, but it is not currently a topic of active investigation. I hadn't even thought of the implications of valhalla. Even though it is not an active topic of investigation, would you please share what the uncomfortable details were? (I am asking out of sheer curiousity). Glad to hear the idea has not been ruled out completely. Thanks Brian, Regards, Vikram Good to know it is a future possibility On Thu, Dec 28, 2023 at 4:38?PM Brian Goetz wrote: > It depends what you mean by "inheritance". > > Will records ever be able to extend an arbitrary class like > SocketInputStream? That's a pretty clear "definitely not." Is there room > for a more restricted category of abstract classes that records could > extend without giving up their semantic benefits and safety guarantees? > Quite possibly, including perhaps the notion of "abstract record", which > permits layered modeling but maintains the safety guarantees of records? > > We explored abstract records during the design of records, and concluded > that while they were a possibility, there are a number of details to work > out, some of which are a little uncomfortable. (Separately, in Valhalla, > we have explored what is required for an abstract class to be extendible by > a value class, which might also inform an answer.) > > So there is room to explore this further, but it is not currently a topic > of active investigation. > > > On 12/28/2023 9:59 AM, Vikram Bakshi wrote: > > Hello, > > Is the decision to not allow inheritance for records set in stone? Or will > this be opened up and explored in the future? > > One of the goals of records (from Brian Goetz Devoxx talk) is to "model > data as data", and allowing inheritance would offer a powerful way of > modelling data. > > Right now we have to write interfaces for fields which are shared/static > methods for shared behaviour/copying and pasting - which I do not think is > ideal. > > Regards, > Vikram > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vab2048 at gmail.com Fri Jan 5 11:13:17 2024 From: vab2048 at gmail.com (Vikram Bakshi) Date: Fri, 5 Jan 2024 11:13:17 +0000 Subject: Allowing inheritance with Records? In-Reply-To: <1397377203.91697872.1703858745603.JavaMail.zimbra@univ-eiffel.fr> References: <1397377203.91697872.1703858745603.JavaMail.zimbra@univ-eiffel.fr> Message-ID: > I almost spill my tea :) :) > Inheritance is about sharing behaviors, so the data behaviors should not change too much. But business requirement, business data/computation changes are frequent, so using inheritance is a kind of an an anti-pattern for data. Is it really an anti-pattern in all situations though? There have been times where I have wanted my record to inherit from a record representing some "id" which itself contains methods/behaviour. I can think of it being useful in some situations. > It's far far easier to use records, empty interfaces and pattern matching (switch on types) when you want to define data and their business rules than tryig to use inheritance for a case it will not work well. I think allowing the developer the flexibility to choose whether or not they would like inheritance would still be a valid "Java" way of doing things. I understand from Brian's email that record inheritance is currently shelved so we will have to wait and see if it ever becomes a priority (other upcoming features like the "state monad" for records are probably more important anyway https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md ). Regards, Vikram On Fri, Dec 29, 2023 at 2:05?PM Remi Forax wrote: > > > ------------------------------ > > *From: *"Vikram Bakshi" > *To: *amber-dev at openjdk.java.net > *Sent: *Thursday, December 28, 2023 3:59:10 PM > *Subject: *Allowing inheritance with Records? > > Hello, > > Is the decision to not allow inheritance for records set in stone? Or will > this be opened up and explored in the future? > > One of the goals of records (from Brian Goetz Devoxx talk) is to "model > data as data", and allowing inheritance would offer a powerful way of > modelling data. > > > I almost spill my tea :) > Inheritance is about sharing behaviors, so the data behaviors should not > change too much. But business requirement, business data/computation > changes are frequent, so using inheritance is a kind of an an anti-pattern > for data. > > It's far far easier to use records, empty interfaces and pattern matching > (switch on types) when you want to define data and their business rules > than tryig to use inheritance for a case it will not work well. > > > > Right now we have to write interfaces for fields which are shared/static > methods for shared behaviour/copying and pasting - which I do not think is > ideal. > > Regards, > Vikram > > > regards, > R?mi > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Fri Jan 5 12:40:21 2024 From: forax at univ-mlv.fr (forax at univ-mlv.fr) Date: Fri, 5 Jan 2024 13:40:21 +0100 (CET) Subject: Allowing inheritance with Records? In-Reply-To: References: <1397377203.91697872.1703858745603.JavaMail.zimbra@univ-eiffel.fr> Message-ID: <1519274829.94129130.1704458421737.JavaMail.zimbra@univ-eiffel.fr> > From: "Vikram Bakshi" > To: "Remi Forax" > Cc: "amber-dev" > Sent: Friday, January 5, 2024 12:13:17 PM > Subject: Re: Allowing inheritance with Records? > > I almost spill my tea :) > :) >> Inheritance is about sharing behaviors, so the data behaviors should not change >> too much. But business requirement, business data/computation changes are > > frequent, so using inheritance is a kind of an an anti-pattern for data. > Is it really an anti-pattern in all situations though? There have been times > where I have wanted my record to inherit from a record representing some "id" > which itself contains methods/behaviour. I can think of it being useful in some > situations. I will never pretend that I know all situations :) Also with an interface + default methods, you also get most the features of inheritance without using inheritance interface Identified { int id(); // abstract default void behavior() { System.our.println(id()); } } record Foo(int id) implements Identified { } record Bar(int id) implements Identified { } And as I said it can also written like this interface Identified { } // empty interface record Foo(int id) implements Identified { } record Bar(int id) implements Identified { } void behavior(Identified identified) { System.out.println(switch(identified) { case Foo(var id) -> id; case Bar(var id) -> id; }); } >> It's far far easier to use records, empty interfaces and pattern matching >> (switch on types) when you want to define data and their business rules than > > tryig to use inheritance for a case it will not work well. > I think allowing the developer the flexibility to choose whether or not they > would like inheritance would still be a valid "Java" way of doing things. > I understand from Brian's email that record inheritance is currently shelved so > we will have to wait and see if it ever becomes a priority (other upcoming > features like the "state monad" for records are probably more important anyway > [ > https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md > | > https://github.com/openjdk/amber-docs/blob/master/eg-drafts/reconstruction-records-and-classes.md > ] ). There is a technical reson to not allow inheritance of record. Components of records are trusted by the JIT, i.e. if the JIT see the value of a record component, instead of reading the value again from the instance, it can use the value directly (the values captured by a lambda works the same way BTW). Now enter inheritance, the constructor of the base class is called before the fields are initialized, so if a component is observed by the VM at that point, the default value (null, 0, false, etc) will be seen because the component is not yet initialized. And then the JIT may replace an access to the component by the default value instead of the proper value. As part of Valhalla, we are working to force the constructor of the base class to be called *after* the field values are initialized. > Regards, > Vikram regards, R?mi > On Fri, Dec 29, 2023 at 2:05 PM Remi Forax < [ mailto:forax at univ-mlv.fr | > forax at univ-mlv.fr ] > wrote: >>> From: "Vikram Bakshi" < [ mailto:vab2048 at gmail.com | vab2048 at gmail.com ] > >>> To: [ mailto:amber-dev at openjdk.java.net | amber-dev at openjdk.java.net ] >>> Sent: Thursday, December 28, 2023 3:59:10 PM >>> Subject: Allowing inheritance with Records? >>> Hello, >>> Is the decision to not allow inheritance for records set in stone? Or will this >>> be opened up and explored in the future? >>> One of the goals of records (from Brian Goetz Devoxx talk) is to "model data as >>> data", and allowing inheritance would offer a powerful way of modelling data. >> I almost spill my tea :) >> Inheritance is about sharing behaviors, so the data behaviors should not change >> too much. But business requirement, business data/computation changes are >> frequent, so using inheritance is a kind of an an anti-pattern for data. >> It's far far easier to use records, empty interfaces and pattern matching >> (switch on types) when you want to define data and their business rules than >> tryig to use inheritance for a case it will not work well. >>> Right now we have to write interfaces for fields which are shared/static methods >>> for shared behaviour/copying and pasting - which I do not think is ideal. >>> Regards, >>> Vikram >> regards, >> R?mi -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 5 14:09:19 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 5 Jan 2024 09:09:19 -0500 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? In-Reply-To: References: <789a1353-0827-40e1-8b92-26b9d2556a6a@oracle.com> Message-ID: The interaction of field initializers and exceptions are already well-defined, there's nothing new here.? If e is an expression that can throw a checked exception, and e is used as an instance field initializer, then all constructors must be declared to throw that checked exception.? Whether the initializer is a switch or a method call makes no difference.? The important thing is that (a) switch expressions either always evaluate to a value or complete abruptly (which has been true for switch expressions since they were introduced) and (b) that we statically know which checked exceptions an expression may throw (the analysis discussed here covers that for switches.) On 1/4/2024 8:34 PM, David Alayachew wrote: > Hello Brian, > > Thank you for your response! > > Yet again, I did not think things through. I was only thinking of > switches being inside of methods. > > But switches are expressions now, so they can be anywhere that we put > an expression. > > So, I could put them on an instance field or a static field. How could > we handle that if the switch wasn't total? > > Yes, what you say about method boundaries not only makes sense, but is > practically required because switch is an expression. > > Thank you for your time and help! > David Alayachew > > > On Thu, Jan 4, 2024, 1:59 PM Brian Goetz wrote: > > There's another piece of the answer here as well, which is the > existing boundaries for type-checking checked exceptions.? And > that existing boundary is solely method boundaries.? (Lambdas are > methods.) > > A method can be declared to throw a set of checked exceptions; the > only place we check for "you can't throw X here" is at the top > level of a method.? (What exceptions might be thrown from the top > level may bubble up from more deeply nested blocks within a > top-level statement, through flow analysis.) > > The current proposal does not propose to change that; we are not > making switches a boundary where we do method-style exception > checking (we'd have to put a `throws` clause on a switch if we did.) > > On 1/1/2024 2:37 AM, David Alayachew wrote: >> Hello, >> >> Thank you for your response! >> >> Yes, this makes a lot of sense, and I figured it would be the >> case. But the article (surprisingly) didn't address that point at >> all. Likely because just broaching the topic of exceptions in >> switch was a task on its own, and this subject will come later. >> >> I also appreciate the set theory formula at the end. You also >> posted that in the other thread, and I still plan to respond to >> that once I get the chance. >> >> Thank you for your time and help! >> David Alayachew >> >> On Mon, Jan 1, 2024 at 2:27?AM Holo The Sage Wolf >> wrote: >> >> Checked exceptions will remain totally checked. >> >> Given a function f, let checkF be the set of checked >> exceptions, then the expression: >> >> switch(root(args)) { >> ? ? case branch0 -> do0(args); >> ? ? case branch1 -> do1(args); >> ? ? ... >> ? ? case branch1 -> doN(args); >> ? ? case throws fBranch0 -> handle0(args); >> ? ? case throws fBranch0 -> handle1(args); >> ? ? ... >> ? ? case throws fBranch0 -> handleM(args); >> } >> >> Will have the following set of checked exceptions: >> >> ? ?(checkRoot - {fBranch0, ... fBranchM}) + checkDo0 + ... + >> checkDoN + checkHandle0 + ... + checkHandleM >> >> Where minus is set difference and plus is union >> >> On Mon, 1 Jan 2024, 09:12 David Alayachew, >> wrote: >> >> Hello Amber Dev Team, >> >> I read Brian Goetz's "Case Effect on Switch" (looks like >> it recently got renamed to "Uniform handling of failure >> in switch"), and I have a quick question. >> >> I think it's cool to bring error handling into switch, >> but will we still get the exhaustiveness/totality on >> checked exceptions that we are used to on the try catch side? >> >> I feel like it would be weird and unintuitive if it >> didn't, since switch expressions already give us >> exhaustiveness/totality, and that's basically the biggest >> reason to use a switch expression in the first place. >> Leaving it out for exceptions would just feel mean. Plus, >> having this feature would really help with clarifying >> intent and with readability. >> >> Thank you for your time and help! >> David Alayachew >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 5 14:27:39 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 5 Jan 2024 09:27:39 -0500 Subject: Allowing inheritance with Records? In-Reply-To: References: <32c477bc-3aeb-4be3-baf2-c67020b43f5a@oracle.com> Message-ID: > ?I hadn't even thought of the implications of valhalla. Even though it > is not an active topic of investigation, would you please share what > the uncomfortable details were? (I am asking out of sheer curiousity). I'll throw out a few thoughts here on the condition that this not become an ongoing design party (or even a discussion); even just discussing "roads not yet taken" takes time away from the roads we *are* taking. First, think about what constraints on extension we would be willing to place.? If record R extends record S, must the components of R contain exactly those of S?? (And, are we willing to identify them solely by name and type?? Do they have to be contiguous?? Do they have to be the first N?? Do they have to occur in the order they are declared in S?)? If so, this limits the expressiveness of record extension, as well as being a pretty new and different kind of constraint than we've seen before.? And if not, we have a bigger problem; how do we know which of the components of R should be lowered to fields of R, and which exist solely to feed S? The "easy" example looks something like: ??? record S(int x) { } ??? record R(int x, int y) extends S { } Here, it seems obvious we should be able to factor the S components out of R.? And, with enough restrictions, we could automagically recognize that x belongs to S, not R.? You might think it is "just" a matter of requiring an explicit indication: ??? record S(int x) { } ??? record R(super int x, int y) extends S { } which would say that "all the components marked super must be components of S, and all the rest are components of R."? But that doesn't solve most of the problems -- do the super components have to appear in the order they do in the S state description (and hence canonical constructor?)? Or are we again doing automatic matching by name and type, just slightly less magically? With all these restrictions, the incremental expressiveness is pretty limited.? It would be much more useful to be able to _derive_ the components of S from the components of R, say through an explicit super() call in the canonical constuctor.? But then we have another problem - super calls are imperative.? So how do we know when and how to tell if a component of R has been "used up" in this way, and should be excluded from the normal treatment? As you can see, what starts as a seemingly simple and achievable goal turns into a flurry of hyper-targeted micro-features (such as the `super` modifier on record components.)? While sometimes such micro-features are unavoidable, they're usually red flags that should be heeded.? The return on the major feature would have to be pretty big to justify the epicyclical features, and it's not really here. From davidalayachew at gmail.com Fri Jan 5 14:48:00 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 5 Jan 2024 09:48:00 -0500 Subject: Uniform Handling of failure in switch -- Exhaustiveness on Exceptions? In-Reply-To: References: <789a1353-0827-40e1-8b92-26b9d2556a6a@oracle.com> Message-ID: Oh wow, I never knew. Good to know, ty vm. And makes sense, ty vm. On Fri, Jan 5, 2024 at 9:09?AM Brian Goetz wrote: > The interaction of field initializers and exceptions are already > well-defined, there's nothing new here. If e is an expression that can > throw a checked exception, and e is used as an instance field initializer, > then all constructors must be declared to throw that checked exception. > Whether the initializer is a switch or a method call makes no difference. > The important thing is that (a) switch expressions either always evaluate > to a value or complete abruptly (which has been true for switch expressions > since they were introduced) and (b) that we statically know which checked > exceptions an expression may throw (the analysis discussed here covers that > for switches.) > > On 1/4/2024 8:34 PM, David Alayachew wrote: > > Hello Brian, > > Thank you for your response! > > Yet again, I did not think things through. I was only thinking of switches > being inside of methods. > > But switches are expressions now, so they can be anywhere that we put an > expression. > > So, I could put them on an instance field or a static field. How could we > handle that if the switch wasn't total? > > Yes, what you say about method boundaries not only makes sense, but is > practically required because switch is an expression. > > Thank you for your time and help! > David Alayachew > > > On Thu, Jan 4, 2024, 1:59 PM Brian Goetz wrote: > >> There's another piece of the answer here as well, which is the existing >> boundaries for type-checking checked exceptions. And that existing >> boundary is solely method boundaries. (Lambdas are methods.) >> >> A method can be declared to throw a set of checked exceptions; the only >> place we check for "you can't throw X here" is at the top level of a >> method. (What exceptions might be thrown from the top level may bubble up >> from more deeply nested blocks within a top-level statement, through flow >> analysis.) >> >> The current proposal does not propose to change that; we are not making >> switches a boundary where we do method-style exception checking (we'd have >> to put a `throws` clause on a switch if we did.) >> >> On 1/1/2024 2:37 AM, David Alayachew wrote: >> >> Hello, >> >> Thank you for your response! >> >> Yes, this makes a lot of sense, and I figured it would be the case. But >> the article (surprisingly) didn't address that point at all. Likely because >> just broaching the topic of exceptions in switch was a task on its own, and >> this subject will come later. >> >> I also appreciate the set theory formula at the end. You also posted that >> in the other thread, and I still plan to respond to that once I get the >> chance. >> >> Thank you for your time and help! >> David Alayachew >> >> On Mon, Jan 1, 2024 at 2:27?AM Holo The Sage Wolf >> wrote: >> >>> Checked exceptions will remain totally checked. >>> >>> Given a function f, let checkF be the set of checked exceptions, then >>> the expression: >>> >>> switch(root(args)) { >>> case branch0 -> do0(args); >>> case branch1 -> do1(args); >>> ... >>> case branch1 -> doN(args); >>> case throws fBranch0 -> handle0(args); >>> case throws fBranch0 -> handle1(args); >>> ... >>> case throws fBranch0 -> handleM(args); >>> } >>> >>> Will have the following set of checked exceptions: >>> >>> (checkRoot - {fBranch0, ... fBranchM}) + checkDo0 + ... + checkDoN + >>> checkHandle0 + ... + checkHandleM >>> >>> Where minus is set difference and plus is union >>> >>> On Mon, 1 Jan 2024, 09:12 David Alayachew, >>> wrote: >>> >>>> Hello Amber Dev Team, >>>> >>>> I read Brian Goetz's "Case Effect on Switch" (looks like it recently >>>> got renamed to "Uniform handling of failure in switch"), and I have a quick >>>> question. >>>> >>>> I think it's cool to bring error handling into switch, but will we >>>> still get the exhaustiveness/totality on checked exceptions that we are >>>> used to on the try catch side? >>>> >>>> I feel like it would be weird and unintuitive if it didn't, since >>>> switch expressions already give us exhaustiveness/totality, and that's >>>> basically the biggest reason to use a switch expression in the first place. >>>> Leaving it out for exceptions would just feel mean. Plus, having this >>>> feature would really help with clarifying intent and with readability. >>>> >>>> Thank you for your time and help! >>>> David Alayachew >>>> >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vab2048 at gmail.com Fri Jan 5 15:51:46 2024 From: vab2048 at gmail.com (vab2048) Date: Fri, 5 Jan 2024 15:51:46 +0000 Subject: Allowing inheritance with Records? In-Reply-To: References: Message-ID: <4319F56F-EB0C-4E1C-913B-6FF6F54F9272@gmail.com> Thank you Remi and Brian for indulging my questions. As requested I?ll terminate the discussion here. Regards, Vikram > On 5 Jan 2024, at 14:27, Brian Goetz wrote: > > ? > >> I hadn't even thought of the implications of valhalla. Even though it is not an active topic of investigation, would you please share what the uncomfortable details were? (I am asking out of sheer curiousity). > > I'll throw out a few thoughts here on the condition that this not become an ongoing design party (or even a discussion); even just discussing "roads not yet taken" takes time away from the roads we *are* taking. > > First, think about what constraints on extension we would be willing to place. If record R extends record S, must the components of R contain exactly those of S? (And, are we willing to identify them solely by name and type? Do they have to be contiguous? Do they have to be the first N? Do they have to occur in the order they are declared in S?) If so, this limits the expressiveness of record extension, as well as being a pretty new and different kind of constraint than we've seen before. And if not, we have a bigger problem; how do we know which of the components of R should be lowered to fields of R, and which exist solely to feed S? > > The "easy" example looks something like: > > record S(int x) { } > record R(int x, int y) extends S { } > > Here, it seems obvious we should be able to factor the S components out of R. And, with enough restrictions, we could automagically recognize that x belongs to S, not R. You might think it is "just" a matter of requiring an explicit indication: > > record S(int x) { } > record R(super int x, int y) extends S { } > > which would say that "all the components marked super must be components of S, and all the rest are components of R." But that doesn't solve most of the problems -- do the super components have to appear in the order they do in the S state description (and hence canonical constructor?) Or are we again doing automatic matching by name and type, just slightly less magically? > > With all these restrictions, the incremental expressiveness is pretty limited. It would be much more useful to be able to _derive_ the components of S from the components of R, say through an explicit super() call in the canonical constuctor. But then we have another problem - super calls are imperative. So how do we know when and how to tell if a component of R has been "used up" in this way, and should be excluded from the normal treatment? > > As you can see, what starts as a seemingly simple and achievable goal turns into a flurry of hyper-targeted micro-features (such as the `super` modifier on record components.) While sometimes such micro-features are unavoidable, they're usually red flags that should be heeded. The return on the major feature would have to be pretty big to justify the epicyclical features, and it's not really here. > From r.j.meeuws at gmail.com Thu Jan 11 12:48:15 2024 From: r.j.meeuws at gmail.com (Roel Meeuws) Date: Thu, 11 Jan 2024 13:48:15 +0100 Subject: Possible optimized record construction in order to use as DTO Message-ID: Dear all, This is my first message to this list, so I hope I am not proposing something which has already been discussed before. But here goes. In several projects at different companies I have seen the use of big bean classes with lots of data inside and getters, setters, toString, equals, hashcode added. In some projects the boilerplate was mitigated with Lombok, but of course recent JDKs provide the notion of Records. Now records are great data carriers with much less boilerplate, however, there is a particular problem when constructing them for very big sets. Consider a dto representing a line in some EU legal obligation report (of which there are many) that may heve hundreds of fields. When constructing such an object there is no idea of what parameter is being filled in in the code that is constructing the object e.g. record IrritatinglyBigEUReportLine( long processingId; ZonedDateTime reportTime; String firstName; String lastName; String leiCode; String legalReportingId; BigDecimal riskAmount; BigDecimal mifidRiskAmount; BigDecimal mifid2FinancialRiskAmount; BigDecimal mifid2SomeOtherAmount; ...... ) {} now let's construct this: var line = new IrritatinglyBigEUReportLine( 12345, ZonedDateTime.now(), "John", "Doe", "529900T8BM49AURSDO55", BigDecimal.valueOf("100.0"), BigDecimal.valueOf("100.0"), BigDecimal.valueOf("100.0"), BigDecimal.valueOf("100.0"), // anyone knows what this value is here? ... ); // also this will give a compile error in my IDE, which parameter is missing now? Could we introduce a better way of initialization like the following, which is like the `with`-er syntax, I have read Brian Goetz writing about. var line = new IrritatingLuBigEUReportLine with { processingId =12345; reportTime = ZonedDateTime.now(); firstName = "John"; lastName = "Doe"; leiCode = "529900T8BM49AURSDO55"; legalReportingId = "ERE43434452ERERTTT"; riskAmount = BigDecimal.valueOf("100.0"); mifidRiskAmount = BigDecimal.valueOf("100.0"); mifid2FinancialRiskAmount = BigDecimal.valueOf("100.0"); mifid2SomeOtherAmount = BigDecimal.valueOf("100.0"); ... }; Roel -------------------------------------------- Roel Meeuws Email: r.j.meeuws at gmail.com Mob. phone: +31 (0)6 10 82 44 01 -------------------------------------------- Virus-free.www.avg.com <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jan 11 13:37:23 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 11 Jan 2024 08:37:23 -0500 Subject: Possible optimized record construction in order to use as DTO In-Reply-To: References: Message-ID: "Can I specify parameters by name instead of position" is a frequent request.? Unfortunately there's a lot more complexity under the waterline here than anyone wants to talk about. The short answer to the localized question ("could we support by-name invocation of the canonical record constructor") is "yes"; since the component names are committed as part of the classes interface, it is relatively simple matter to support this for the canonical constructor of records.? But that begs the question: would this be a good idea (let alone "would this actually make people happy.")? Let's talk about how this glass will seem X% empty. First, because this is specific to records (and to a specific constructor for records), this creates a seam between how you can interact with records and with other classes.? That means that users have to keep a "mental dictionary" of which classes are records and which are not.? Second, because the use site is likely to be in a separate compilation unit, you've now created an impediment to migrating between records and their equivalent classes, because such a migration would no longer be source compatible (clients would fail to recompile).? Third, people will surely find this boundary to be irregular; "why can I do it with this class and not that" or "with this constructor and not that" or "with constructors but not with static factories" will be frequent questions; even if these questions have good answers, it means that we've increased the set of fiddly rules that all developers have to internalize.? And developers are rarely made happy by "OK, you can do that on tuesday mornings only" solutions; if anything, it creates more unhappiness that they can't do it all week long. And that doesn't mention the elephant in the room: default values.? Most of the time, when people ask about by-name invocation, the question of defaults for parameters they do not name isn't far behind.? And this is where it really gets messy, because this injects new complexity into an already-complex area of the language (overload selection), and some painful questions about binary compatibility ("surely I can add another parameter with a default?") However, if your record really does have reasonable defaults for some fields (such as empty lists, not just null), the proposed `with` mechanism would let you do what you want without a new feature: define a "minimal constructor" with the required fields (which fills in defaults for the others), and then combine that with a reconstruction expression: ??? new Foo(reqA, reqB, reqC) ??????? with { optD = 3, optQ = "Hi Bob" } and you're good. On 1/11/2024 7:48 AM, Roel Meeuws wrote: > Dear all, > > This is my first message to this list, so I hope I am not proposing > something which has already been discussed before. But here goes. > > In several projects at different companies I have seen the use of big > bean classes with lots of data inside and getters, setters, toString, > equals, hashcode added. In some projects the boilerplate was mitigated > with Lombok, but of course recent JDKs provide the notion of Records. > > Now records are great data carriers?with much less boilerplate, > however, there is a particular problem when constructing them for very > big sets. > Consider a dto representing a line in some EU legal obligation report > (of which there are many) that may heve?hundreds of fields. When > constructing such an object there is no idea of what parameter is > being filled in in the code that is constructing the object > > e.g. > > record IrritatinglyBigEUReportLine( > ? long processingId; > ZonedDateTime reportTime; > String firstName; > String lastName; > String leiCode; > ? String legalReportingId; > BigDecimal riskAmount; > ? BigDecimal mifidRiskAmount; > ? BigDecimal mifid2FinancialRiskAmount; > ? BigDecimal mifid2SomeOtherAmount; > ? ...... > ) {} > > now let's construct this: > var line = new IrritatinglyBigEUReportLine( > 12345, > ZonedDateTime.now(), > "John", > "Doe", > "529900T8BM49AURSDO55", > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), // anyone knows what this value is here? > ? ... > ); > > // also this will give a compile error in my IDE, which parameter is > missing now? > > Could we introduce a better way of initialization like the following, > which is like the `with`-er syntax, I have read Brian Goetz writing about. > var line = new IrritatingLuBigEUReportLine with { > ? processingId =12345; > ? reportTime = ZonedDateTime.now(); > ? firstName = "John"; > ? lastName = "Doe"; > ? leiCode = "529900T8BM49AURSDO55"; > ? legalReportingId = "ERE43434452ERERTTT"; > ? riskAmount = BigDecimal.valueOf("100.0"); > ? mifidRiskAmount = BigDecimal.valueOf("100.0"); > ? mifid2FinancialRiskAmount = BigDecimal.valueOf("100.0"); > ? mifid2SomeOtherAmount = BigDecimal.valueOf("100.0"); > ? ... > }; > > Roel > > -------------------------------------------- > Roel Meeuws > Email: r.j.meeuws at gmail.com > Mob. phone: +31 (0)6 10 82 44 01 > -------------------------------------------- > > > Virus-free.www.avg.com > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From pedro.lamarao at prodist.com.br Thu Jan 11 13:50:20 2024 From: pedro.lamarao at prodist.com.br (=?UTF-8?Q?Pedro_Lamar=C3=A3o?=) Date: Thu, 11 Jan 2024 10:50:20 -0300 Subject: Possible optimized record construction in order to use as DTO In-Reply-To: References: Message-ID: Hello! My observation is below. Em qui., 11 de jan. de 2024 ?s 09:50, Roel Meeuws escreveu: > now let's construct this: > var line = new IrritatinglyBigEUReportLine( > 12345, > ZonedDateTime.now(), > "John", > "Doe", > "529900T8BM49AURSDO55", > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), // anyone knows what this value is here? > ... > ); > I think this is an interesting usability problem that has many solutions. My IDE, for example, puts a small widget in front of that line, showing the name of the parameter. I suspect many good editors today will show the name of the parameter with some UX gesture, such as mouse rovering, or placing the caret at the position and doing some key combination. Many such problems are best solved by the editor. -- Pedro Lamar?o -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jan 11 14:04:58 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 11 Jan 2024 09:04:58 -0500 Subject: Possible optimized record construction in order to use as DTO In-Reply-To: References: Message-ID: <854f6e33-d38d-4c59-a5cf-6abf14f60137@oracle.com> It depends on a lot of things.? (If all arguments have defaults, then your initial value can even be a static constant.)? But the fact that your first reaction is "but won't that be slow" is one that you should work hard to suppress; for the vast majority of developers, our initial assumptions about the impact of micro performance issues on macro performance are usually dead wrong.? Sometimes they merely cause us to waste a lot of time; sometimes the consequences are much worse. This is especially true in the domain you're asking about (big enterprise applications); your example was from a report, and the likelihood that the performance bottleneck in an enterprise reporting application is that each object was allocated twice is probably similar to the likelihood of the programmer growing an extra head.? Just write clear, maintainable code. On 1/11/2024 8:56 AM, Roel Meeuws wrote: > Thanks Brian, > > Using a reconstruction to do the trick will be much more readable > that?what we?have now, but what will the jvm actually do? Will it > instantiate the record twice? will all defaults (maybe expensive?) be > generated or optimized away? > > In the end large beans are a pain, records seem to be a better fit, > but construction does not "feel" right. At least with withers it will > improve. > > thanks?again > > Roel > > -------------------------------------------- > Roel Meeuws > Email: r.j.meeuws at gmail.com > Mob. phone: +31 (0)6 10 82 44 01 > -------------------------------------------- > > > > Virus-free.www.avg.com > > > > > Op do 11 jan 2024 om 14:37 schreef Brian Goetz : > > "Can I specify parameters by name instead of position" is a > frequent request.? Unfortunately there's a lot more complexity > under the waterline here than anyone wants to talk about. > > The short answer to the localized question ("could we support > by-name invocation of the canonical record constructor") is "yes"; > since the component names are committed as part of the classes > interface, it is relatively simple matter to support this for the > canonical constructor of records.? But that begs the question: > would this be a good idea (let alone "would this actually make > people happy.")? Let's talk about how this glass will seem X% empty. > > First, because this is specific to records (and to a specific > constructor for records), this creates a seam between how you can > interact with records and with other classes.? That means that > users have to keep a "mental dictionary" of which classes are > records and which are not.? Second, because the use site is likely > to be in a separate compilation unit, you've now created an > impediment to migrating between records and their equivalent > classes, because such a migration would no longer be source > compatible (clients would fail to recompile).? Third, people will > surely find this boundary to be irregular; "why can I do it with > this class and not that" or "with this constructor and not that" > or "with constructors but not with static factories" will be > frequent questions; even if these questions have good answers, it > means that we've increased the set of fiddly rules that all > developers have to internalize.? And developers are rarely made > happy by "OK, you can do that on tuesday mornings only" solutions; > if anything, it creates more unhappiness that they can't do it all > week long. > > And that doesn't mention the elephant in the room: default > values.? Most of the time, when people ask about by-name > invocation, the question of defaults for parameters they do not > name isn't far behind.? And this is where it really gets messy, > because this injects new complexity into an already-complex area > of the language (overload selection), and some painful questions > about binary compatibility ("surely I can add another parameter > with a default?") > > However, if your record really does have reasonable defaults for > some fields (such as empty lists, not just null), the proposed > `with` mechanism would let you do what you want without a new > feature: define a "minimal constructor" with the required fields > (which fills in defaults for the others), and then combine that > with a reconstruction expression: > > ??? new Foo(reqA, reqB, reqC) > ??????? with { optD = 3, optQ = "Hi Bob" } > > and you're good. > > > > On 1/11/2024 7:48 AM, Roel Meeuws wrote: >> Dear all, >> >> This is my first message to this list, so I hope I am not >> proposing something which has already been discussed before. But >> here goes. >> >> In several projects at different companies I have seen the use of >> big bean classes with lots of data inside and getters, setters, >> toString, equals, hashcode added. In some projects the >> boilerplate was mitigated with Lombok, but of course recent JDKs >> provide the notion of Records. >> >> Now records are great data carriers?with much less boilerplate, >> however, there is a particular problem when constructing them for >> very big sets. >> Consider a dto representing a line in some EU legal obligation >> report (of which there are many) that may heve?hundreds of >> fields. When constructing such an object there is no idea of what >> parameter is being filled in in the code that is constructing the >> object >> >> e.g. >> >> record IrritatinglyBigEUReportLine( >> ? long processingId; >> ? ZonedDateTime reportTime; >> ? String firstName; >> ? String lastName; >> ? String leiCode; >> ? String legalReportingId; >> ? BigDecimal riskAmount; >> ? BigDecimal mifidRiskAmount; >> ? BigDecimal mifid2FinancialRiskAmount; >> ? BigDecimal mifid2SomeOtherAmount; >> ? ...... >> ) {} >> >> now let's construct this: >> var line = new IrritatinglyBigEUReportLine( >> ? 12345, >> ZonedDateTime.now(), >> ? "John", >> ? "Doe", >> "529900T8BM49AURSDO55", >> BigDecimal.valueOf("100.0"), >> BigDecimal.valueOf("100.0"), >> BigDecimal.valueOf("100.0"), >> BigDecimal.valueOf("100.0"), // anyone knows what this value is here? >> ? ... >> ); >> >> // also this will give a compile error in my IDE, which parameter >> is missing now? >> >> Could we introduce a better way of initialization like the >> following, which is like the `with`-er syntax, I have read Brian >> Goetz writing about. >> var line = new IrritatingLuBigEUReportLine with { >> ? processingId =12345; >> ? reportTime = ZonedDateTime.now(); >> ? firstName = "John"; >> ? lastName = "Doe"; >> ? leiCode = "529900T8BM49AURSDO55"; >> ? legalReportingId = "ERE43434452ERERTTT"; >> ? riskAmount = BigDecimal.valueOf("100.0"); >> ? mifidRiskAmount = BigDecimal.valueOf("100.0"); >> ? mifid2FinancialRiskAmount = BigDecimal.valueOf("100.0"); >> ? mifid2SomeOtherAmount = BigDecimal.valueOf("100.0"); >> ? ... >> }; >> >> Roel >> >> -------------------------------------------- >> Roel Meeuws >> Email: r.j.meeuws at gmail.com >> Mob. phone: +31 (0)6 10 82 44 01 >> -------------------------------------------- >> >> >> Virus-free.www.avg.com >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From r.j.meeuws at gmail.com Thu Jan 11 13:56:43 2024 From: r.j.meeuws at gmail.com (Roel Meeuws) Date: Thu, 11 Jan 2024 14:56:43 +0100 Subject: Possible optimized record construction in order to use as DTO In-Reply-To: References: Message-ID: Thanks Brian, Using a reconstruction to do the trick will be much more readable that what we have now, but what will the jvm actually do? Will it instantiate the record twice? will all defaults (maybe expensive?) be generated or optimized away? In the end large beans are a pain, records seem to be a better fit, but construction does not "feel" right. At least with withers it will improve. thanks again Roel -------------------------------------------- Roel Meeuws Email: r.j.meeuws at gmail.com Mob. phone: +31 (0)6 10 82 44 01 -------------------------------------------- Virus-free.www.avg.com <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> Op do 11 jan 2024 om 14:37 schreef Brian Goetz : > "Can I specify parameters by name instead of position" is a frequent > request. Unfortunately there's a lot more complexity under the waterline > here than anyone wants to talk about. > > The short answer to the localized question ("could we support by-name > invocation of the canonical record constructor") is "yes"; since the > component names are committed as part of the classes interface, it is > relatively simple matter to support this for the canonical constructor of > records. But that begs the question: would this be a good idea (let alone > "would this actually make people happy.") Let's talk about how this glass > will seem X% empty. > > First, because this is specific to records (and to a specific constructor > for records), this creates a seam between how you can interact with records > and with other classes. That means that users have to keep a "mental > dictionary" of which classes are records and which are not. Second, > because the use site is likely to be in a separate compilation unit, you've > now created an impediment to migrating between records and their equivalent > classes, because such a migration would no longer be source compatible > (clients would fail to recompile). Third, people will surely find this > boundary to be irregular; "why can I do it with this class and not that" or > "with this constructor and not that" or "with constructors but not with > static factories" will be frequent questions; even if these questions have > good answers, it means that we've increased the set of fiddly rules that > all developers have to internalize. And developers are rarely made happy > by "OK, you can do that on tuesday mornings only" solutions; if anything, > it creates more unhappiness that they can't do it all week long. > > And that doesn't mention the elephant in the room: default values. Most > of the time, when people ask about by-name invocation, the question of > defaults for parameters they do not name isn't far behind. And this is > where it really gets messy, because this injects new complexity into an > already-complex area of the language (overload selection), and some painful > questions about binary compatibility ("surely I can add another parameter > with a default?") > > However, if your record really does have reasonable defaults for some > fields (such as empty lists, not just null), the proposed `with` mechanism > would let you do what you want without a new feature: define a "minimal > constructor" with the required fields (which fills in defaults for the > others), and then combine that with a reconstruction expression: > > new Foo(reqA, reqB, reqC) > with { optD = 3, optQ = "Hi Bob" } > > and you're good. > > > > On 1/11/2024 7:48 AM, Roel Meeuws wrote: > > Dear all, > > This is my first message to this list, so I hope I am not proposing > something which has already been discussed before. But here goes. > > In several projects at different companies I have seen the use of big bean > classes with lots of data inside and getters, setters, toString, equals, > hashcode added. In some projects the boilerplate was mitigated with Lombok, > but of course recent JDKs provide the notion of Records. > > Now records are great data carriers with much less boilerplate, however, > there is a particular problem when constructing them for very big sets. > Consider a dto representing a line in some EU legal obligation report (of > which there are many) that may heve hundreds of fields. When constructing > such an object there is no idea of what parameter is being filled in in the > code that is constructing the object > > e.g. > > record IrritatinglyBigEUReportLine( > long processingId; > ZonedDateTime reportTime; > String firstName; > String lastName; > String leiCode; > String legalReportingId; > BigDecimal riskAmount; > BigDecimal mifidRiskAmount; > BigDecimal mifid2FinancialRiskAmount; > BigDecimal mifid2SomeOtherAmount; > ...... > ) {} > > now let's construct this: > var line = new IrritatinglyBigEUReportLine( > 12345, > ZonedDateTime.now(), > "John", > "Doe", > "529900T8BM49AURSDO55", > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), // anyone knows what this value is here? > ... > ); > > // also this will give a compile error in my IDE, which parameter is > missing now? > > Could we introduce a better way of initialization like the following, > which is like the `with`-er syntax, I have read Brian Goetz writing about. > var line = new IrritatingLuBigEUReportLine with { > processingId =12345; > reportTime = ZonedDateTime.now(); > firstName = "John"; > lastName = "Doe"; > leiCode = "529900T8BM49AURSDO55"; > legalReportingId = "ERE43434452ERERTTT"; > riskAmount = BigDecimal.valueOf("100.0"); > mifidRiskAmount = BigDecimal.valueOf("100.0"); > mifid2FinancialRiskAmount = BigDecimal.valueOf("100.0"); > mifid2SomeOtherAmount = BigDecimal.valueOf("100.0"); > ... > }; > > Roel > > -------------------------------------------- > Roel Meeuws > Email: r.j.meeuws at gmail.com > Mob. phone: +31 (0)6 10 82 44 01 > -------------------------------------------- > > > > Virus-free.www.avg.com > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From r.j.meeuws at gmail.com Thu Jan 11 13:59:55 2024 From: r.j.meeuws at gmail.com (Roel Meeuws) Date: Thu, 11 Jan 2024 14:59:55 +0100 Subject: Possible optimized record construction in order to use as DTO In-Reply-To: References: Message-ID: Hi Pedro, My editor will also insert these, but: * not all my colleagues have that in their editors * The perceived formatting is impacted by inserting these words between the text * This is not working for me in e.g. Eclipse when the method call is not finalized yet or there is a compile error (because I am still typing). Roel -------------------------------------------- Roel Meeuws Email: r.j.meeuws at gmail.com Mob. phone: +31 (0)6 10 82 44 01 -------------------------------------------- Virus-free.www.avg.com <#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> Op do 11 jan 2024 om 14:50 schreef Pedro Lamar?o < pedro.lamarao at prodist.com.br>: > Hello! My observation is below. > > Em qui., 11 de jan. de 2024 ?s 09:50, Roel Meeuws > escreveu: > > >> now let's construct this: >> var line = new IrritatinglyBigEUReportLine( >> 12345, >> ZonedDateTime.now(), >> "John", >> "Doe", >> "529900T8BM49AURSDO55", >> BigDecimal.valueOf("100.0"), >> BigDecimal.valueOf("100.0"), >> BigDecimal.valueOf("100.0"), >> BigDecimal.valueOf("100.0"), // anyone knows what this value is here? >> ... >> ); >> > > I think this is an interesting usability problem that has many solutions. > My IDE, for example, puts a small widget in front of that line, showing > the name of the parameter. > I suspect many good editors today will show the name of the parameter with > some UX gesture, such as mouse rovering, or placing the caret at the > position and doing some key combination. > Many such problems are best solved by the editor. > > -- > Pedro Lamar?o > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Thu Jan 11 16:25:30 2024 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Thu, 11 Jan 2024 16:25:30 +0000 Subject: Possible optimized record construction in order to use as DTO In-Reply-To: References: Message-ID: Currently there are some libs to overcome the limitation like https://github.com/Randgalt/record-builder . However, I still think it looks more like a hack and loses required parameters to be named. It should be part of a language, but there are too many ways to do it and there are some limitations and compromises... which makes it quite difficult to please everyone. On Thu, 11 Jan 2024 at 13:54, Roel Meeuws wrote: > Dear all, > > This is my first message to this list, so I hope I am not proposing > something which has already been discussed before. But here goes. > > In several projects at different companies I have seen the use of big bean > classes with lots of data inside and getters, setters, toString, equals, > hashcode added. In some projects the boilerplate was mitigated with Lombok, > but of course recent JDKs provide the notion of Records. > > Now records are great data carriers with much less boilerplate, however, > there is a particular problem when constructing them for very big sets. > Consider a dto representing a line in some EU legal obligation report (of > which there are many) that may heve hundreds of fields. When constructing > such an object there is no idea of what parameter is being filled in in the > code that is constructing the object > > e.g. > > record IrritatinglyBigEUReportLine( > long processingId; > ZonedDateTime reportTime; > String firstName; > String lastName; > String leiCode; > String legalReportingId; > BigDecimal riskAmount; > BigDecimal mifidRiskAmount; > BigDecimal mifid2FinancialRiskAmount; > BigDecimal mifid2SomeOtherAmount; > ...... > ) {} > > now let's construct this: > var line = new IrritatinglyBigEUReportLine( > 12345, > ZonedDateTime.now(), > "John", > "Doe", > "529900T8BM49AURSDO55", > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), > BigDecimal.valueOf("100.0"), // anyone knows what this value is here? > ... > ); > > // also this will give a compile error in my IDE, which parameter is > missing now? > > Could we introduce a better way of initialization like the following, > which is like the `with`-er syntax, I have read Brian Goetz writing about. > var line = new IrritatingLuBigEUReportLine with { > processingId =12345; > reportTime = ZonedDateTime.now(); > firstName = "John"; > lastName = "Doe"; > leiCode = "529900T8BM49AURSDO55"; > legalReportingId = "ERE43434452ERERTTT"; > riskAmount = BigDecimal.valueOf("100.0"); > mifidRiskAmount = BigDecimal.valueOf("100.0"); > mifid2FinancialRiskAmount = BigDecimal.valueOf("100.0"); > mifid2SomeOtherAmount = BigDecimal.valueOf("100.0"); > ... > }; > > Roel > > -------------------------------------------- > Roel Meeuws > Email: r.j.meeuws at gmail.com > Mob. phone: +31 (0)6 10 82 44 01 > -------------------------------------------- > > > > Virus-free.www.avg.com > > <#m_7710971634432452811_DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> > -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From hjohn at xs4all.nl Thu Jan 11 23:08:32 2024 From: hjohn at xs4all.nl (John Hendrikx) Date: Thu, 11 Jan 2024 23:08:32 +0000 Subject: Possible optimized record construction in order to use as DTO In-Reply-To: References: Message-ID: You can consider using an annotation processor to create builders for such records -- for records with that many parameters, that's probably a good idea anyway. Although Lombok is often used for this, there are alternatives as well. I can recommend taking a look at https://github.com/Randgalt/record-builder If you are only "mapping" such records instead of creating them manually, you may take a look at mapping frameworks (like mapstruct). --John ------ Original Message ------ >From "Roel Meeuws" To "Pedro Lamar?o" Cc amber-dev at openjdk.org Date 11/01/2024 14:59:55 Subject Re: Possible optimized record construction in order to use as DTO >Hi Pedro, > >My editor will also insert these, but: > * not all my colleagues have that in their editors > * The perceived formatting is impacted by inserting these words >between the text > * This is not working for me in e.g. Eclipse when the method call is >not finalized yet or there is a compile error (because I am still >typing). > >Roel > >-------------------------------------------- >Roel Meeuws >Email: r.j.meeuws at gmail.com >Mob. phone: +31 (0)6 10 82 44 01 >-------------------------------------------- > > > >Virus-free.www.avg.com > ><#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2> > >Op do 11 jan 2024 om 14:50 schreef Pedro Lamar?o >: >>Hello! My observation is below. >> >>Em qui., 11 de jan. de 2024 ?s 09:50, Roel Meeuws >> escreveu: >> >>>now let's construct this: >>>var line = new IrritatinglyBigEUReportLine( >>> 12345, >>> ZonedDateTime.now(), >>> "John", >>> "Doe", >>> "529900T8BM49AURSDO55", >>> BigDecimal.valueOf("100.0"), >>> BigDecimal.valueOf("100.0"), >>> BigDecimal.valueOf("100.0"), >>> BigDecimal.valueOf("100.0"), // anyone knows what this value is >>>here? >>> ... >>>); >> >>I think this is an interesting usability problem that has many >>solutions. >>My IDE, for example, puts a small widget in front of that line, >>showing the name of the parameter. >>I suspect many good editors today will show the name of the parameter >>with some UX gesture, such as mouse rovering, or placing the caret at >>the position and doing some key combination. >>Many such problems are best solved by the editor. >> >>-- >>Pedro Lamar?o -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Fri Jan 12 14:14:09 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Fri, 12 Jan 2024 22:14:09 +0800 Subject: Official support for Unsafe Message-ID: Hi, These days I have kept an eye on the 1brc challenge and 1 particular phenomenon that has struck me is the usage of Unsafe by the participants. While Java has developed a lot in terms of alternatives for Unsafe such as VarHandle and FFM, a particular use case of Unsafe is to access memory in an unsafe manner which cannot be done without some kind of unsafe support. I believe that while the compiler can theoretically eliminate a lot of bound checks and even if they exist, a predictable branch is normally cheap, there will always be places where that is not the case, as the access can be very far from the place where the compiler can get the relevant information, or the bound checks will compete with the bottlenecked CPU frontend or backend. In the cases where every nanosecond counts, a bound check may be prohibitively expensive, and the capability to bypass them would be valuable. Looking at other languages, C++ is unsafe by nature, C#, Go and even Rust all provide the ability to step out of the safe realm. As a result, I think the necessity of unsafe accesses is evident. Regarding the implementation, the support can start minimally with the ability to access arrays or to interpret an array as another without bound checking. Unsafe accesses require the program to start with --enable-unsafe-accesses=, the same as how we restrict native accesses, which IMO exhibits a similar nature after the introduction of passing heap segment to native downcalls. Last but not least, unsafe accesses will still perform bound checks prior to C2 or in the presence of a special unsafe flag, the former acts as a safety net that may catch most mistakes the programmers make and the latter is a sanity check when the need arises. Please let me know if there is any misunderstanding regarding the situation, or if this is not the suitable mailing list for the proposal. Regards, Quan Anh -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jan 12 14:43:03 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 12 Jan 2024 14:43:03 +0000 Subject: Official support for Unsafe In-Reply-To: References: Message-ID: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> Before I comment on your proposal, let's see what we can get through the means we have today. As you have tried to do, you can use MemorySegment, and its accessor. If your loops are counted, you should get loop unrolling, bound check elimination, and even auto-vectorization. The problem is when a loop is not counted, or runs for a number of iterations that is too small (so that even if checks are hoisted outside, you still execute them a lot). There is an escape hatch in the FFM API, one that requires "--enable-native-access". It goes like this: * Create an _everything_ segment, doing `MemorySegment.NULL.reinterpret(Long.MAX_SIZE)` * Stash the everything segment in a static final constant (so that JIT can see through its fields) * Tweak your code to operate on the everything segment - e.g. this becomes effectively a replacement for an Unsafe::getXYZ taking a long address Note: this is still not 100% on par with Unsafe, because FFM has still to check that the address you pass is positive. But this a much much simpler check. Hope this helps. Maurizio On 12/01/2024 14:14, Qu?n Anh Mai wrote: > Hi, > > These days I have kept an eye on the 1brc challenge and 1 particular > phenomenon that has struck me is the usage of Unsafe by the > participants. While Java has developed a lot in terms of alternatives > for Unsafe such as VarHandle and FFM, a particular use case of Unsafe > is to access memory in an unsafe manner which cannot be done without > some kind of unsafe support. > > I believe that while the compiler can theoretically eliminate a lot of > bound checks and even if they exist, a predictable branch is normally > cheap, there will always be places where that is not the case, as the > access can be very far from the place where the compiler can get the > relevant information, or the bound checks will compete with the > bottlenecked CPU frontend or backend. In the cases where every > nanosecond counts, a bound check may be prohibitively expensive, and > the capability to bypass them would be valuable. > > Looking at other languages, C++ is unsafe by nature, C#, Go and even > Rust all provide the ability to step out of the safe realm. As a > result, I think the necessity of unsafe accesses is evident. > > Regarding the implementation, the support can start minimally with the > ability to access arrays or to interpret an array as another without > bound checking. Unsafe accesses require the program to start with > --enable-unsafe-accesses=, the same as how we restrict > native accesses, which IMO exhibits a similar nature after the > introduction of passing heap segment to native downcalls. Last but not > least, unsafe accesses will still perform bound checks prior to C2 or > in the presence of a special unsafe flag, the former acts as a safety > net that may catch most mistakes the programmers make and the latter > is a sanity check when the need arises. > > Please let me know if there is any misunderstanding regarding the > situation, or if this is not the suitable mailing list for the proposal. > > Regards, > Quan Anh From anhmdq at gmail.com Fri Jan 12 15:24:39 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Fri, 12 Jan 2024 23:24:39 +0800 Subject: Official support for Unsafe In-Reply-To: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> Message-ID: Hi, Thanks a lot for your comment. Speaking from my experience, a frequent use case is that a loop (counted or not) calls into a function that cannot be inlined, and inside this function, there are a lot of bound checks, another use case is that the access indices are not trivial to derive from the induction variable. In the first case, the compiler will have a very hard time eliminating the checks, as from the perspective of the function, each check only occurs once, so there is nowhere to hoist to, but from the perspective of the program, those checks are executed inside its hot loop. And in the second case, there is nothing can be done. Just one particular data point, my current solution for 1brc using Unsafe results in around 1.1e12 instructions as reported by perf stat. I have tried implementing the same concept using VarHandle and FFM accessors, the instruction count was almost doubled at 2.1e12. Your proposed solution still requires 2 checks, a lifetime check (albeit it can be hoisted, but not across function calls) and a positive index check, a workaround as you have proposed before is to create a segment on the fly and depend on the compiler to eliminate everything, the downside is that it is prone to the failure of inlining those function, which may be counter-productive. Furthermore, the most important downside is that all these tricks with MemorySegment will not work with Java arrays, which I think are the more common arguments of Unsafe methods. Thanks a lot, Quan Anh On Fri, 12 Jan 2024 at 22:43, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > Before I comment on your proposal, let's see what we can get through the > means we have today. > > As you have tried to do, you can use MemorySegment, and its accessor. If > your loops are counted, you should get loop unrolling, bound check > elimination, and even auto-vectorization. > > The problem is when a loop is not counted, or runs for a number of > iterations that is too small (so that even if checks are hoisted > outside, you still execute them a lot). > > There is an escape hatch in the FFM API, one that requires > "--enable-native-access". It goes like this: > > * Create an _everything_ segment, doing > `MemorySegment.NULL.reinterpret(Long.MAX_SIZE)` > * Stash the everything segment in a static final constant (so that JIT > can see through its fields) > * Tweak your code to operate on the everything segment - e.g. this > becomes effectively a replacement for an Unsafe::getXYZ taking a long > address > > Note: this is still not 100% on par with Unsafe, because FFM has still > to check that the address you pass is positive. But this a much much > simpler check. > > Hope this helps. > > Maurizio > > > > On 12/01/2024 14:14, Qu?n Anh Mai wrote: > > Hi, > > > > These days I have kept an eye on the 1brc challenge and 1 particular > > phenomenon that has struck me is the usage of Unsafe by the > > participants. While Java has developed a lot in terms of alternatives > > for Unsafe such as VarHandle and FFM, a particular use case of Unsafe > > is to access memory in an unsafe manner which cannot be done without > > some kind of unsafe support. > > > > I believe that while the compiler can theoretically eliminate a lot of > > bound checks and even if they exist, a predictable branch is normally > > cheap, there will always be places where that is not the case, as the > > access can be very far from the place where the compiler can get the > > relevant information, or the bound checks will compete with the > > bottlenecked CPU frontend or backend. In the cases where every > > nanosecond counts, a bound check may be prohibitively expensive, and > > the capability to bypass them would be valuable. > > > > Looking at other languages, C++ is unsafe by nature, C#, Go and even > > Rust all provide the ability to step out of the safe realm. As a > > result, I think the necessity of unsafe accesses is evident. > > > > Regarding the implementation, the support can start minimally with the > > ability to access arrays or to interpret an array as another without > > bound checking. Unsafe accesses require the program to start with > > --enable-unsafe-accesses=, the same as how we restrict > > native accesses, which IMO exhibits a similar nature after the > > introduction of passing heap segment to native downcalls. Last but not > > least, unsafe accesses will still perform bound checks prior to C2 or > > in the presence of a special unsafe flag, the former acts as a safety > > net that may catch most mistakes the programmers make and the latter > > is a sanity check when the need arises. > > > > Please let me know if there is any misunderstanding regarding the > > situation, or if this is not the suitable mailing list for the proposal. > > > > Regards, > > Quan Anh > -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jan 12 16:52:49 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 12 Jan 2024 16:52:49 +0000 Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> Message-ID: <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> On 12/01/2024 15:24, Qu?n Anh Mai wrote: > Your proposed solution still requires 2 checks, a lifetime check > (albeit it can be hoisted, but not across function calls) and a > positive index check, a workaround as you have proposed before is to > create a segment on the fly and depend on the compiler to eliminate > everything, the downside is that it is prone to the failure of > inlining those function, which may be counter-productive. Furthermore, > the most important downside is that all these tricks with > MemorySegment will not work with Java arrays, which I think are the > more common arguments of Unsafe methods. While I agree that in principle some checks are still there (as I said in my email), these strategies have been proven quite effective when working with random-access idioms. But I take your point that all these tricks are based on the assumption that you care about off-heap access. One possible idea I mused about in the past, was to expose special var handles (whose creation would be subject to --enable-native-access or similar mechanism, of course). The problem with doing things such as these, is that if we do them for well-intentioned use cases (such as the frequency trading application that wants to shave off up to the very last nanosecond), I think everybody else will get the message that "this is how we do it", and will start using unsafe access even when there's no real need to use it. Heck, looking at the results of the 1brc competition, your pure Java solution (no Unsafe, just FFM) ended up scoring quite high (3rd!) - at some point the question has to become: how much safety I'm willing to sacrifice to get an extra 1% ? I know that's a subjective decision, of course, but it's one that, as a community, we need to gripple with (as that has deep repercussions on the rest of the ecosystem). Maurizio From brian.goetz at oracle.com Fri Jan 12 17:00:58 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 12 Jan 2024 17:00:58 +0000 Subject: Official support for Unsafe In-Reply-To: <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> Message-ID: Indeed, this is the whole question: how much of *everyone?s* safety are we willing to trade to make the already-pretty-good, 99.999% case run a little faster. On Jan 12, 2024, at 11:52 AM, Maurizio Cimadamore > wrote: Heck, looking at the results of the 1brc competition, your pure Java solution (no Unsafe, just FFM) ended up scoring quite high (3rd!) - at some point the question has to become: how much safety I'm willing to sacrifice to get an extra 1% ? I know that's a subjective decision, of course, but it's one that, as a community, we need to gripple with (as that has deep repercussions on the rest of the ecosystem). -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Fri Jan 12 17:26:24 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Sat, 13 Jan 2024 01:26:24 +0800 Subject: Official support for Unsafe In-Reply-To: <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> Message-ID: I think that while your concern is perfectly valid, it seems not evident. Looking at other languages, even if they do not require the addition of startup flags, despite the existence of unsafe capabilities, there seems to be no evidence that developers over-exploit those features. It can be argued that current Unsafe support in Java also does not make people recklessly seek its support. While C++ is special because of its default being unchecked, Java has a long culture of using checked accesses. Last but not least, the most important aspect is readability, as going from arr[i] to Unsafe.getIntUnchecked(arr, i) imposes a considerable cognitive load. To conclude, it is unlikely that unsafe accesses will be recklessly used given all the hassles and warnings. FYI, in my submission to 1brc, using Unsafe decreases the execution time from 3.25s to 2.57s on the test machine. Best regards, Quan Anh On Sat, 13 Jan 2024 at 00:53, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 12/01/2024 15:24, Qu?n Anh Mai wrote: > > Your proposed solution still requires 2 checks, a lifetime check > > (albeit it can be hoisted, but not across function calls) and a > > positive index check, a workaround as you have proposed before is to > > create a segment on the fly and depend on the compiler to eliminate > > everything, the downside is that it is prone to the failure of > > inlining those function, which may be counter-productive. Furthermore, > > the most important downside is that all these tricks with > > MemorySegment will not work with Java arrays, which I think are the > > more common arguments of Unsafe methods. > > While I agree that in principle some checks are still there (as I said > in my email), these strategies have been proven quite effective when > working with random-access idioms. > > But I take your point that all these tricks are based on the assumption > that you care about off-heap access. > > One possible idea I mused about in the past, was to expose special var > handles (whose creation would be subject to --enable-native-access or > similar mechanism, of course). The problem with doing things such as > these, is that if we do them for well-intentioned use cases (such as the > frequency trading application that wants to shave off up to the very > last nanosecond), I think everybody else will get the message that "this > is how we do it", and will start using unsafe access even when there's > no real need to use it. > > Heck, looking at the results of the 1brc competition, your pure Java > solution (no Unsafe, just FFM) ended up scoring quite high (3rd!) - at > some point the question has to become: how much safety I'm willing to > sacrifice to get an extra 1% ? I know that's a subjective decision, of > course, but it's one that, as a community, we need to gripple with (as > that has deep repercussions on the rest of the ecosystem). > > Maurizio > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Fri Jan 12 18:40:12 2024 From: forax at univ-mlv.fr (Remi Forax) Date: Fri, 12 Jan 2024 19:40:12 +0100 (CET) Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> Message-ID: <1363558440.101416709.1705084812774.JavaMail.zimbra@univ-eiffel.fr> Hello mery kitty, ""Safety"" is half of the problem here. The other half is the cost of maintaining an official API to do native access and the fact that it will make future features/optimizations harder or impossible (cf the work to support sun.misc.Unsafe in Valhalla). We already provide command line options to access to jdk.internal.misc.Unsafe (--add-exports/-add-opens) that are already too powerful IMO, but at least the contract is clear, any classes in jdk.internal.blah may change in the next JDK. So you can access to the extra 1% if you want, at the cost of having to track the changes of the internal classes of the JDK. That is a fair deal for me. regards, R?mi > From: "Brian Goetz" > To: "Maurizio Cimadamore" > Cc: "Qu?n Anh Mai" , "amber-dev" > Sent: Friday, January 12, 2024 6:00:58 PM > Subject: Re: Official support for Unsafe > Indeed, this is the whole question: how much of *everyone?s* safety are we > willing to trade to make the already-pretty-good, 99.999% case run a little > faster. >> On Jan 12, 2024, at 11:52 AM, Maurizio Cimadamore < [ >> mailto:maurizio.cimadamore at oracle.com | maurizio.cimadamore at oracle.com ] > >> wrote: >> Heck, looking at the results of the 1brc competition, your pure Java solution >> (no Unsafe, just FFM) ended up scoring quite high (3rd!) - at some point the >> question has to become: how much safety I'm willing to sacrifice to get an >> extra 1% ? I know that's a subjective decision, of course, but it's one that, >> as a community, we need to gripple with (as that has deep repercussions on the >> rest of the ecosystem). -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Fri Jan 12 19:12:47 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Sat, 13 Jan 2024 03:12:47 +0800 Subject: Official support for Unsafe In-Reply-To: <1363558440.101416709.1705084812774.JavaMail.zimbra@univ-eiffel.fr> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <1363558440.101416709.1705084812774.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hi, The issue with the maintainability of jdk.internal.misc.Unsafe is that it is too powerful. I imagine getIntUnchecked(int[], int) can be implemented in Java as a simple index access, and C2 will parse it similar to a index operation apart from the bound check. Please correct me if I do not comprehend the complexity of this matter. Thanks a lot, Quan Anh On Sat, 13 Jan 2024 at 02:40, Remi Forax wrote: > Hello mery kitty, > ""Safety"" is half of the problem here. > > The other half is the cost of maintaining an official API to do native > access and the fact that it will make future features/optimizations harder > or impossible (cf the work to support sun.misc.Unsafe in Valhalla). > > We already provide command line options to access to > jdk.internal.misc.Unsafe (--add-exports/-add-opens) that are already too > powerful IMO, but at least the contract is clear, any classes in > jdk.internal.blah may change in the next JDK. > > So you can access to the extra 1% if you want, at the cost of having to > track the changes of the internal classes of the JDK. That is a fair deal > for me. > > regards, > R?mi > > ------------------------------ > > *From: *"Brian Goetz" > *To: *"Maurizio Cimadamore" > *Cc: *"Qu?n Anh Mai" , "amber-dev" < > amber-dev at openjdk.org> > *Sent: *Friday, January 12, 2024 6:00:58 PM > *Subject: *Re: Official support for Unsafe > > Indeed, this is the whole question: how much of *everyone?s* safety are we > willing to trade to make the already-pretty-good, 99.999% case run a little > faster. > > On Jan 12, 2024, at 11:52 AM, Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > > Heck, looking at the results of the 1brc competition, your pure Java > solution (no Unsafe, just FFM) ended up scoring quite high (3rd!) - at some > point the question has to become: how much safety I'm willing to sacrifice > to get an extra 1% ? I know that's a subjective decision, of course, but > it's one that, as a community, we need to gripple with (as that has deep > repercussions on the rest of the ecosystem). > > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Fri Jan 12 19:19:11 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Sat, 13 Jan 2024 03:19:11 +0800 Subject: Official support for Unsafe In-Reply-To: <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> Message-ID: It is really late here so I will try that tomorrow. Note that usage of the everything segment requires --enable-native-access, which in general gives an unsafe notice anyway. Regards, Quan Anh On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 12/01/2024 17:26, Qu?n Anh Mai wrote: > > FYI, in my submission to 1brc, using Unsafe decreases the execution > > time from 3.25s to 2.57s on the test machine. > > Just curious - what is the difference compared with the everything > segment trick? > > (While I know it can't do on-heap access, perhaps you can tweak the code > to be all off-heap?) > > Maurizio > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Fri Jan 12 19:21:11 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 12 Jan 2024 19:21:11 +0000 Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> Message-ID: <2eaa4b39-5407-458c-b4e7-894b96bfe0a0@oracle.com> On 12/01/2024 19:19, Qu?n Anh Mai wrote: > It is really late here so I will try that tomorrow. > > Note that usage of the everything segment requires > --enable-native-access, which in general gives an unsafe notice anyway. Sure, I'm mostly curious as to how much of the gap is caused by bound check for on-heap vs. off-heap access. In my experience the latter almost always dominate the former. Maurizio > > Regards, > Quan Anh > > On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore > wrote: > > > On 12/01/2024 17:26, Qu?n Anh Mai wrote: > > FYI, in my submission to 1brc, using Unsafe decreases the execution > > time from 3.25s to 2.57s on the test machine. > > Just curious - what is the difference compared with the everything > segment trick? > > (While I know it can't do on-heap access, perhaps you can tweak > the code > to be all off-heap?) > > Maurizio > -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Sat Jan 13 05:07:29 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Sat, 13 Jan 2024 13:07:29 +0800 Subject: Official support for Unsafe In-Reply-To: References: Message-ID: Another idea I have just come up with: Without the flag the program still runs as normal with the checks intact, and in the presence of --enable-unsafe-access, C2 will remove the bound checks away. This has advantages that it allows libraries to not force usage of the flag on the program and the program can consciously choose to remove the checks if the need arises. For example, multiple libraries used by a program all of which can be used in performance-sensitive situations such as json parsing libraries, but from the perspective of the program, only 1 of them truly needs to be optimal, or even none of them need to be. This approach allows the program to keep most checks intact and is only exposed to minimal risk if the flag is added. The disadvantage of the idea is that it removes one obstacle for library writers, which may make the unsafe accesses more attractive to them. Regards, Quan Anh On Fri, 12 Jan 2024 at 22:14, Qu?n Anh Mai wrote: > Hi, > > These days I have kept an eye on the 1brc challenge and 1 particular > phenomenon that has struck me is the usage of Unsafe by the participants. > While Java has developed a lot in terms of alternatives for Unsafe such as > VarHandle and FFM, a particular use case of Unsafe is to access memory in > an unsafe manner which cannot be done without some kind of unsafe support. > > I believe that while the compiler can theoretically eliminate a lot of > bound checks and even if they exist, a predictable branch is normally > cheap, there will always be places where that is not the case, as the > access can be very far from the place where the compiler can get the > relevant information, or the bound checks will compete with the > bottlenecked CPU frontend or backend. In the cases where every nanosecond > counts, a bound check may be prohibitively expensive, and the capability to > bypass them would be valuable. > > Looking at other languages, C++ is unsafe by nature, C#, Go and even Rust > all provide the ability to step out of the safe realm. As a result, I think > the necessity of unsafe accesses is evident. > > Regarding the implementation, the support can start minimally with the > ability to access arrays or to interpret an array as another without bound > checking. Unsafe accesses require the program to start with > --enable-unsafe-accesses=, the same as how we restrict native > accesses, which IMO exhibits a similar nature after the introduction of > passing heap segment to native downcalls. Last but not least, unsafe > accesses will still perform bound checks prior to C2 or in the presence of > a special unsafe flag, the former acts as a safety net that may catch most > mistakes the programmers make and the latter is a sanity check when the > need arises. > > Please let me know if there is any misunderstanding regarding the > situation, or if this is not the suitable mailing list for the proposal. > > Regards, > Quan Anh > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 15 07:48:57 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 15 Jan 2024 02:48:57 -0500 Subject: Allow variables declared in do-while loop body to be in scope in the condition? Message-ID: Hello Amber Dev Team, Would it make sense and would there be value in allowing variables declared in the body of a do-while loop to still be considered in scope for the condition of the same do-while loop? Basically, the following example is currently not valid Java, but should it be? ```java do { final var someVar = someMethod(); doSomethingWith(someVar); //etc. } while (someVar.someBooleanReturningMethod()); ``` I think this is clear, simple, and is better than the alternative. ```java /* Must be mutable! :( */ /* And you probably need a default value too! */ var someVar = someYuckyDefaultOrWorseYetNull; do { //code } while (someVar != 42); //And annoyingly enough, the variable is still in scope outside of the loop! Blegh. ``` Since do-while loops are very old, I wouldn't be surprised if someone else has asked this before. But regardless, I couldn't find anything, so I ask now -- is there any value in doing this? Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Jan 15 07:51:13 2024 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 15 Jan 2024 08:51:13 +0100 Subject: Allow variables declared in do-while loop body to be in scope in the condition? In-Reply-To: References: Message-ID: Note that this is not the compatible change, as the variable may shadow same-named field which was in the scope of condition before. On Mon, Jan 15, 2024, 08:49 David Alayachew wrote: > Hello Amber Dev Team, > > Would it make sense and would there be value in allowing variables > declared in the body of a do-while loop to still be considered in scope for > the condition of the same do-while loop? > > Basically, the following example is currently not valid Java, but should > it be? > > ```java > > do > { > > final var someVar = someMethod(); > > doSomethingWith(someVar); > > //etc. > > } > > while (someVar.someBooleanReturningMethod()); > > ``` > > I think this is clear, simple, and is better than the alternative. > > ```java > > /* Must be mutable! :( */ > /* And you probably need a default value too! */ > var someVar = someYuckyDefaultOrWorseYetNull; > > do > { > > //code > > } > > while (someVar != 42); > > //And annoyingly enough, the variable is still in scope outside of the > loop! Blegh. > > ``` > > Since do-while loops are very old, I wouldn't be surprised if someone else > has asked this before. But regardless, I couldn't find anything, so I ask > now -- is there any value in doing this? > > Thank you for your time and help! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 15 09:59:17 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 15 Jan 2024 04:59:17 -0500 Subject: Allow variables declared in do-while loop body to be in scope in the condition? In-Reply-To: References: Message-ID: I forgot to click Reply All, so most of this conversation happened off of the mailing list. Read below to see the discussion, as well as an example of what Tagir was talking about, as well as potential workarounds. On Mon, Jan 15, 2024 at 4:48?AM Tagir Valeev wrote: > Probably. By the way, the original piece of code I usually write as: > > while(true) { > final var someVar = someMethod(); > doSomethingWith(someVar); > //etc. > if (!someVar.someBooleanReturningMethod()) break; > } > > Not so pretty too, but still allows keeping the scope of someVar limited > and avoiding splitting the declaration and the assignment. > > With best regards, > Tagir Valeev. > > On Mon, Jan 15, 2024 at 10:20?AM David Alayachew > wrote: > >> Ah, I see what you mean now. >> >> In that case, nevermind. I understand now why my idea is backwards >> incompatible. What a shame. >> >> Acknowledging that this idea no longer has ground to stand on, let me ask >> the hypothetical -- would do-while loops be better with this feature >> implemented? I am not at all trying to get this feature implemented >> anymore. I just want to know if it would have been a good idea in the first >> place, backwards compatibility issues aside. >> >> On Mon, Jan 15, 2024 at 4:04?AM Tagir Valeev wrote: >> >>> E.g.: >>> >>> public class DoWhile { >>> static boolean b = false; >>> >>> public static void main(String[] args) { >>> do { >>> boolean b = true; >>> } while (b); >>> } >>> } >>> >>> >>> Now, this code is correct and finishes successfully. If we expand the >>> scope of local b to the condition, it will shadow the field b. As a result, >>> the code will still be correct but stuck in endless loop. >>> >>> With best regards, >>> Tagir Valeev. >>> >>> On Mon, Jan 15, 2024 at 9:00?AM David Alayachew < >>> davidalayachew at gmail.com> wrote: >>> >>>> Hello, >>>> >>>> Thank you for your response! >>>> >>>> I am afraid I do not follow. Could you post an example? >>>> >>>> From my quick experimentation now, it seems like what you describe >>>> isn't possible. >>>> >>>> The only way I could see what you are saying to be true is if a >>>> variable is in scope for the while condition, but not in scope for the >>>> do-while body. And I can't seem to create a situation where a variable is >>>> in scope for the while condition, but is not in scope for the body. >>>> >>>> Or maybe it is something else entirely? Please lmk. >>>> >>>> Thank you for your time and help! >>>> David Alayachew >>>> >>>> On Mon, Jan 15, 2024 at 2:51?AM Tagir Valeev wrote: >>>> >>>>> Note that this is not the compatible change, as the variable may >>>>> shadow same-named field which was in the scope of condition before. >>>>> >>>>> On Mon, Jan 15, 2024, 08:49 David Alayachew >>>>> wrote: >>>>> >>>>>> Hello Amber Dev Team, >>>>>> >>>>>> Would it make sense and would there be value in allowing variables >>>>>> declared in the body of a do-while loop to still be considered in scope for >>>>>> the condition of the same do-while loop? >>>>>> >>>>>> Basically, the following example is currently not valid Java, but >>>>>> should it be? >>>>>> >>>>>> ```java >>>>>> >>>>>> do >>>>>> { >>>>>> >>>>>> final var someVar = someMethod(); >>>>>> >>>>>> doSomethingWith(someVar); >>>>>> >>>>>> //etc. >>>>>> >>>>>> } >>>>>> >>>>>> while (someVar.someBooleanReturningMethod()); >>>>>> >>>>>> ``` >>>>>> >>>>>> I think this is clear, simple, and is better than the alternative. >>>>>> >>>>>> ```java >>>>>> >>>>>> /* Must be mutable! :( */ >>>>>> /* And you probably need a default value too! */ >>>>>> var someVar = someYuckyDefaultOrWorseYetNull; >>>>>> >>>>>> do >>>>>> { >>>>>> >>>>>> //code >>>>>> >>>>>> } >>>>>> >>>>>> while (someVar != 42); >>>>>> >>>>>> //And annoyingly enough, the variable is still in scope outside of >>>>>> the loop! Blegh. >>>>>> >>>>>> ``` >>>>>> >>>>>> Since do-while loops are very old, I wouldn't be surprised if someone >>>>>> else has asked this before. But regardless, I couldn't find anything, so I >>>>>> ask now -- is there any value in doing this? >>>>>> >>>>>> Thank you for your time and help! >>>>>> David Alayachew >>>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Mon Jan 15 13:52:18 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Mon, 15 Jan 2024 21:52:18 +0800 Subject: Official support for Unsafe In-Reply-To: <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> Message-ID: Hi, I have tried using a universe segment instead of Unsafe, and store the custom hashmap buffer in off-heap instead of using a byte array. The output of perf stat on the program Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': 13573.70 msec task-clock:u # 10.942 CPUs utilized 0 context-switches:u # 0.000 /sec 0 cpu-migrations:u # 0.000 /sec 238460 page-faults:u # 17.568 K/sec 61995179870 cycles:u # 4.567 GHz 261830581 stalled-cycles-frontend:u # 0.42% frontend cycles idle 93823680 stalled-cycles-backend:u # 0.15% backend cycles idle 137976098809 instructions:u # 2.23 insn per cycle # 0.00 stalled cycles per insn 18373313803 branches:u # 1.354 G/sec 43579782 branch-misses:u # 0.24% of all branches 1.240504612 seconds time elapsed 12.841563000 seconds user 0.652428000 seconds sys For comparison, this is the unsafe version: Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': 13327.46 msec task-clock:u # 11.202 CPUs utilized 0 context-switches:u # 0.000 /sec 0 cpu-migrations:u # 0.000 /sec 269896 page-faults:u # 20.251 K/sec 61258348752 cycles:u # 4.596 GHz 639839262 stalled-cycles-frontend:u # 1.04% frontend cycles idle 108018676 stalled-cycles-backend:u # 0.18% backend cycles idle 113476168983 instructions:u # 1.85 insn per cycle # 0.01 stalled cycles per insn 11442665370 branches:u # 858.578 M/sec 44590172 branch-misses:u # 0.39% of all branches 1.189768677 seconds time elapsed 12.628512000 seconds user 0.620083000 seconds sys This program running on my machine expresses dependency bound so the difference in execution time is not as significant as on the test machine but it can be seen that removing Unsafe results in over 21% increase in instruction count. Regards, Quan Anh On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 12/01/2024 17:26, Qu?n Anh Mai wrote: > > FYI, in my submission to 1brc, using Unsafe decreases the execution > > time from 3.25s to 2.57s on the test machine. > > Just curious - what is the difference compared with the everything > segment trick? > > (While I know it can't do on-heap access, perhaps you can tweak the code > to be all off-heap?) > > Maurizio > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jan 15 15:09:01 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 15 Jan 2024 15:09:01 +0000 Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> Message-ID: <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> I think the increased instruction count is normal, as C2 had to do more work to optimize the bound checks away? Is there any difference compared to the version that doesn't use the universe segment? Maurizio On 15/01/2024 13:52, Qu?n Anh Mai wrote: > Hi, > > I have tried using a universe segment instead of Unsafe, and store the > custom hashmap buffer in off-heap instead of using a byte array. The > output of perf stat on the program > > ?Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': > > ? ? ? ? ? 13573.70 msec task-clock:u ? ? ? ? ? ? ?# ? 10.942 CPUs utilized > ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ? ? ? ?# ? ?0.000 /sec > ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ? ? ? ? ?# ? ?0.000 /sec > ? ? ? ? ? ? 238460 ? ? ?page-faults:u ? ? ? ? ? ? # ? 17.568 K/sec > ? ? ? ?61995179870 ? ? ?cycles:u ? ? ? ? ? ? ? ? ?# ? ?4.567 GHz > ? ? ? ? ?261830581 ? ? ?stalled-cycles-frontend:u # ? ?0.42% frontend > cycles idle > ? ? ? ? ? 93823680 ? ? ?stalled-cycles-backend:u ?# ? ?0.15% backend > cycles idle > ? ? ? 137976098809 ? ? ?instructions:u ? ? ? ? ? ?# ? ?2.23 ?insn per > cycle > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # ? ?0.00 ?stalled > cycles per insn > ? ? ? ?18373313803 ? ? ?branches:u ? ? ? ? ? ? ? ?# ? ?1.354 G/sec > ? ? ? ? ? 43579782 ? ? ?branch-misses:u ? ? ? ? ? # ? ?0.24% of all > branches > > ? ? ? ?1.240504612 seconds time elapsed > > ? ? ? 12.841563000 seconds user > ? ? ? ?0.652428000 seconds sys > > For comparison, this is the unsafe version: > > ?Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': > > ? ? ? ? ? 13327.46 msec task-clock:u ? ? ? ? ? ? ?# ? 11.202 CPUs utilized > ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ? ? ? ?# ? ?0.000 /sec > ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ? ? ? ? ?# ? ?0.000 /sec > ? ? ? ? ? ? 269896 ? ? ?page-faults:u ? ? ? ? ? ? # ? 20.251 K/sec > ? ? ? ?61258348752 ? ? ?cycles:u ? ? ? ? ? ? ? ? ?# ? ?4.596 GHz > ? ? ? ? ?639839262 ? ? ?stalled-cycles-frontend:u # ? ?1.04% frontend > cycles idle > ? ? ? ? ?108018676 ? ? ?stalled-cycles-backend:u ?# ? ?0.18% backend > cycles idle > ? ? ? 113476168983 ? ? ?instructions:u ? ? ? ? ? ?# ? ?1.85 ?insn per > cycle > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # ? ?0.01 ?stalled > cycles per insn > ? ? ? ?11442665370 ? ? ?branches:u ? ? ? ? ? ? ? ?# ?858.578 M/sec > ? ? ? ? ? 44590172 ? ? ?branch-misses:u ? ? ? ? ? # ? ?0.39% of all > branches > > ? ? ? ?1.189768677 seconds time elapsed > > ? ? ? 12.628512000 seconds user > ? ? ? ?0.620083000 seconds sys > > This program running on my machine expresses dependency bound so the > difference in execution time is not as significant as on the test > machine but it can be seen that removing Unsafe results in over 21% > increase in instruction count. > > Regards, > Quan Anh > > On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore > wrote: > > > On 12/01/2024 17:26, Qu?n Anh Mai wrote: > > FYI, in my submission to 1brc, using Unsafe decreases the execution > > time from 3.25s to 2.57s on the test machine. > > Just curious - what is the difference compared with the everything > segment trick? > > (While I know it can't do on-heap access, perhaps you can tweak > the code > to be all off-heap?) > > Maurizio > -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Mon Jan 15 15:44:58 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Mon, 15 Jan 2024 23:44:58 +0800 Subject: Official support for Unsafe In-Reply-To: <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> Message-ID: Running the same program on 1e6 lines results in only 9e9 instructions, so I think the vast majority of the instruction count is of the compiled code. Not using the universe segment is roughly equivalent to my previous version, which would result in around 50% more instructions compared to using one, and almost double the instruction count of using Unsafe. Regards, Quan Anh On Mon, 15 Jan 2024 at 23:09, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > I think the increased instruction count is normal, as C2 had to do more > work to optimize the bound checks away? > > Is there any difference compared to the version that doesn't use the > universe segment? > > Maurizio > On 15/01/2024 13:52, Qu?n Anh Mai wrote: > > Hi, > > I have tried using a universe segment instead of Unsafe, and store the > custom hashmap buffer in off-heap instead of using a byte array. The output > of perf stat on the program > > Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': > > 13573.70 msec task-clock:u # 10.942 CPUs utilized > 0 context-switches:u # 0.000 /sec > 0 cpu-migrations:u # 0.000 /sec > 238460 page-faults:u # 17.568 K/sec > 61995179870 cycles:u # 4.567 GHz > 261830581 stalled-cycles-frontend:u # 0.42% frontend > cycles idle > 93823680 stalled-cycles-backend:u # 0.15% backend > cycles idle > 137976098809 instructions:u # 2.23 insn per cycle > # 0.00 stalled > cycles per insn > 18373313803 branches:u # 1.354 G/sec > 43579782 branch-misses:u # 0.24% of all > branches > > 1.240504612 seconds time elapsed > > 12.841563000 seconds user > 0.652428000 seconds sys > > For comparison, this is the unsafe version: > > Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': > > 13327.46 msec task-clock:u # 11.202 CPUs utilized > 0 context-switches:u # 0.000 /sec > 0 cpu-migrations:u # 0.000 /sec > 269896 page-faults:u # 20.251 K/sec > 61258348752 cycles:u # 4.596 GHz > 639839262 stalled-cycles-frontend:u # 1.04% frontend > cycles idle > 108018676 stalled-cycles-backend:u # 0.18% backend > cycles idle > 113476168983 instructions:u # 1.85 insn per cycle > # 0.01 stalled > cycles per insn > 11442665370 branches:u # 858.578 M/sec > 44590172 branch-misses:u # 0.39% of all > branches > > 1.189768677 seconds time elapsed > > 12.628512000 seconds user > 0.620083000 seconds sys > > This program running on my machine expresses dependency bound so the > difference in execution time is not as significant as on the test machine > but it can be seen that removing Unsafe results in over 21% increase in > instruction count. > > Regards, > Quan Anh > > On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > >> >> On 12/01/2024 17:26, Qu?n Anh Mai wrote: >> > FYI, in my submission to 1brc, using Unsafe decreases the execution >> > time from 3.25s to 2.57s on the test machine. >> >> Just curious - what is the difference compared with the everything >> segment trick? >> >> (While I know it can't do on-heap access, perhaps you can tweak the code >> to be all off-heap?) >> >> Maurizio >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jan 15 16:29:17 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 15 Jan 2024 16:29:17 +0000 Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> Message-ID: <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> On 15/01/2024 15:44, Qu?n Anh Mai wrote: > Running the same program on 1e6 lines results in only 9e9 > instructions, so I think the vast majority of the instruction count is > of the compiled code. Not using the universe segment is roughly > equivalent to my previous version, which would result in around 50% > more instructions compared to using one, and almost double the > instruction count of using Unsafe. Without looking at the program some more, it's hard for me to make some sense of these numbers. I'm surprised that you don't see any difference when using unbounded segment compared to regular ones. I wonder if the gap you are seeing is due to the JVM warming up, rather than peak performances being worse. Have you tried measuring peak performance with e.g. JMH? I would not expect to see 20% difference there... Maurizio > > Regards, > Quan Anh > > On Mon, 15 Jan 2024 at 23:09, Maurizio Cimadamore > wrote: > > I think the increased instruction count is normal, as C2 had to do > more work to optimize the bound checks away? > > Is there any difference compared to the version that doesn't use > the universe segment? > > Maurizio > > On 15/01/2024 13:52, Qu?n Anh Mai wrote: >> Hi, >> >> I have tried using a universe segment instead of Unsafe, and >> store the custom hashmap buffer in off-heap instead of using a >> byte array. The output of perf stat on the program >> >> ?Performance counter stats for 'sh >> calculate_average_merykittyunsafe.sh': >> >> ? ? ? ? ? 13573.70 msec task-clock:u ? ? ? ? ? ? ?# 10.942 CPUs >> utilized >> ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ? ? ? ?# ?0.000 /sec >> ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ? ? ? ? ?# ?0.000 /sec >> ? ? ? ? ? ? 238460 ? ? ?page-faults:u ? ? ? ? ? ? # 17.568 K/sec >> ? ? ? ?61995179870 ? ? ?cycles:u ? ? ? ? ? ? ? ? ?# ?4.567 GHz >> ? ? ? ? ?261830581 ? ? ?stalled-cycles-frontend:u # ?0.42% >> frontend cycles idle >> ? ? ? ? ? 93823680 ? ? ?stalled-cycles-backend:u ?# ?0.15% >> backend cycles idle >> ? ? ? 137976098809 ? ? ?instructions:u ? ? ? ? ? ?# ?2.23 ?insn >> per cycle >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # ?0.00 >> ?stalled cycles per insn >> ? ? ? ?18373313803 ? ? ?branches:u ? ? ? ? ? ? ? ?# ?1.354 G/sec >> ? ? ? ? ? 43579782 ? ? ?branch-misses:u ? ? ? ? ? # ?0.24% of all >> branches >> >> ? ? ? ?1.240504612 seconds time elapsed >> >> ? ? ? 12.841563000 seconds user >> ? ? ? ?0.652428000 seconds sys >> >> For comparison, this is the unsafe version: >> >> ?Performance counter stats for 'sh >> calculate_average_merykittyunsafe.sh': >> >> ? ? ? ? ? 13327.46 msec task-clock:u ? ? ? ? ? ? ?# ? 11.202 CPUs >> utilized >> ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ? ? ? ?# ? ?0.000 /sec >> ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ? ? ? ? ?# ? ?0.000 /sec >> ? ? ? ? ? ? 269896 ? ? ?page-faults:u ? ? ? ? ? ? # ? 20.251 K/sec >> ? ? ? ?61258348752 ? ? ?cycles:u ? ? ? ? ? ? ? ? ?# ? ?4.596 GHz >> ? ? ? ? ?639839262 ? ? ?stalled-cycles-frontend:u # ? ?1.04% >> frontend cycles idle >> ? ? ? ? ?108018676 ? ? ?stalled-cycles-backend:u ?# ? ?0.18% >> backend cycles idle >> ? ? ? 113476168983 ? ? ?instructions:u ? ? ? ? ? ?# ? ?1.85 ?insn >> per cycle >> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? # ? ?0.01 >> ?stalled cycles per insn >> ? ? ? ?11442665370 ? ? ?branches:u ? ? ? ? ? ? ? ?# ?858.578 M/sec >> ? ? ? ? ? 44590172 ? ? ?branch-misses:u ? ? ? ? ? # ? ?0.39% of >> all branches >> >> ? ? ? ?1.189768677 seconds time elapsed >> >> ? ? ? 12.628512000 seconds user >> ? ? ? ?0.620083000 seconds sys >> >> This program running on my machine expresses dependency bound so >> the difference in execution time is not as significant as on the >> test machine but it can be seen that removing Unsafe results in >> over 21% increase in instruction count. >> >> Regards, >> Quan Anh >> >> On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore >> wrote: >> >> >> On 12/01/2024 17:26, Qu?n Anh Mai wrote: >> > FYI, in my submission to 1brc, using Unsafe decreases the >> execution >> > time from 3.25s to 2.57s on the test machine. >> >> Just curious - what is the difference compared with the >> everything >> segment trick? >> >> (While I know it can't do on-heap access, perhaps you can >> tweak the code >> to be all off-heap?) >> >> Maurizio >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Mon Jan 15 17:05:02 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Tue, 16 Jan 2024 01:05:02 +0800 Subject: Official support for Unsafe In-Reply-To: <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> Message-ID: Sure, I just thought that looking at the instruction count would be more helpful, since each machine would express different performance behaviours. For example, my machine shows dependency bound going from [2] to [1] below, which leads to a much smaller margin of execution time compared to the margin measured by other machines (such as the test machine). The third implementation is similar to the first one, except I use safe accesses in the form of bounded memory segment accesses and varhandles. The JMH numbers for these versions look like this, I define an execute function which is: @Benchmark public PoorManMap execute() throws IOException { try (var file = FileChannel.open(Path.of(FILE), StandardOpenOption.READ); var arena = Arena.ofShared()) { var data = file.map(MapMode.READ_ONLY, 0, file.size(), arena); return processFile(data, 0, data.byteSize()); } } CalculateAverage_merykitty.execute avgt 5 7.422 ? 0.093 ms/op // unsafe [1] CalculateAverage_merykitty.execute avgt 5 7.686 ? 0.181 ms/op // universe segment [2] CalculateAverage_merykitty.execute avgt 5 9.009 ? 0.058 ms/op // varhandle [3] [1]: https://github.com/merykitty/1brc/tree/main [2]: https://github.com/merykitty/1brc/tree/removeunsafe [3]: https://github.com/merykitty/1brc/tree/varhandles Best regards, Quan Anh On Tue, 16 Jan 2024 at 00:29, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 15/01/2024 15:44, Qu?n Anh Mai wrote: > > Running the same program on 1e6 lines results in only 9e9 instructions, so > I think the vast majority of the instruction count is of the compiled code. > Not using the universe segment is roughly equivalent to my previous > version, which would result in around 50% more instructions compared to > using one, and almost double the instruction count of using Unsafe. > > Without looking at the program some more, it's hard for me to make some > sense of these numbers. I'm surprised that you don't see any difference > when using unbounded segment compared to regular ones. I wonder if the gap > you are seeing is due to the JVM warming up, rather than peak performances > being worse. Have you tried measuring peak performance with e.g. JMH? I > would not expect to see 20% difference there... > > Maurizio > > > Regards, > Quan Anh > > On Mon, 15 Jan 2024 at 23:09, Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > >> I think the increased instruction count is normal, as C2 had to do more >> work to optimize the bound checks away? >> >> Is there any difference compared to the version that doesn't use the >> universe segment? >> >> Maurizio >> On 15/01/2024 13:52, Qu?n Anh Mai wrote: >> >> Hi, >> >> I have tried using a universe segment instead of Unsafe, and store the >> custom hashmap buffer in off-heap instead of using a byte array. The output >> of perf stat on the program >> >> Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': >> >> 13573.70 msec task-clock:u # 10.942 CPUs utilized >> 0 context-switches:u # 0.000 /sec >> 0 cpu-migrations:u # 0.000 /sec >> 238460 page-faults:u # 17.568 K/sec >> 61995179870 cycles:u # 4.567 GHz >> 261830581 stalled-cycles-frontend:u # 0.42% frontend >> cycles idle >> 93823680 stalled-cycles-backend:u # 0.15% backend >> cycles idle >> 137976098809 instructions:u # 2.23 insn per >> cycle >> # 0.00 stalled >> cycles per insn >> 18373313803 branches:u # 1.354 G/sec >> 43579782 branch-misses:u # 0.24% of all >> branches >> >> 1.240504612 seconds time elapsed >> >> 12.841563000 seconds user >> 0.652428000 seconds sys >> >> For comparison, this is the unsafe version: >> >> Performance counter stats for 'sh calculate_average_merykittyunsafe.sh': >> >> 13327.46 msec task-clock:u # 11.202 CPUs utilized >> 0 context-switches:u # 0.000 /sec >> 0 cpu-migrations:u # 0.000 /sec >> 269896 page-faults:u # 20.251 K/sec >> 61258348752 cycles:u # 4.596 GHz >> 639839262 stalled-cycles-frontend:u # 1.04% frontend >> cycles idle >> 108018676 stalled-cycles-backend:u # 0.18% backend >> cycles idle >> 113476168983 instructions:u # 1.85 insn per >> cycle >> # 0.01 stalled >> cycles per insn >> 11442665370 branches:u # 858.578 M/sec >> 44590172 branch-misses:u # 0.39% of all >> branches >> >> 1.189768677 seconds time elapsed >> >> 12.628512000 seconds user >> 0.620083000 seconds sys >> >> This program running on my machine expresses dependency bound so the >> difference in execution time is not as significant as on the test machine >> but it can be seen that removing Unsafe results in over 21% increase in >> instruction count. >> >> Regards, >> Quan Anh >> >> On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore < >> maurizio.cimadamore at oracle.com> wrote: >> >>> >>> On 12/01/2024 17:26, Qu?n Anh Mai wrote: >>> > FYI, in my submission to 1brc, using Unsafe decreases the execution >>> > time from 3.25s to 2.57s on the test machine. >>> >>> Just curious - what is the difference compared with the everything >>> segment trick? >>> >>> (While I know it can't do on-heap access, perhaps you can tweak the code >>> to be all off-heap?) >>> >>> Maurizio >>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Jan 15 17:33:51 2024 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 15 Jan 2024 18:33:51 +0100 (CET) Subject: Allow variables declared in do-while loop body to be in scope in the condition? In-Reply-To: References: Message-ID: <1686151439.103304746.1705340031720.JavaMail.zimbra@univ-eiffel.fr> > From: "David Alayachew" > To: "Tagir Valeev" , "amber-dev" > Sent: Monday, January 15, 2024 10:59:17 AM > Subject: Re: Allow variables declared in do-while loop body to be in scope in > the condition? > I forgot to click Reply All, so most of this conversation happened off of the > mailing list. Read below to see the discussion, as well as an example of what > Tagir was talking about, as well as potential workarounds. > On Mon, Jan 15, 2024 at 4:48 AM Tagir Valeev < [ mailto:amaembo at gmail.com | > amaembo at gmail.com ] > wrote: >> Probably. By the way, the original piece of code I usually write as: >> while(true) { >> final var someVar = someMethod(); >> doSomethingWith(someVar); >> //etc. >> if (!someVar.someBooleanReturningMethod()) break; >> } >> Not so pretty too, but still allows keeping the scope of someVar limited and >> avoiding splitting the declaration and the assignment. >> With best regards, >> Tagir Valeev. yes, technically, you can do a little bit better (in terms of bytecode) to avoid two consecutive gotoes. while(true) { final var someVar = someMethod(); doSomethingWith(someVar); //etc. if (someVar.someBooleanReturningMethod()) continue; break; } regards, R?mi >> On Mon, Jan 15, 2024 at 10:20 AM David Alayachew < [ >> mailto:davidalayachew at gmail.com | davidalayachew at gmail.com ] > wrote: >>> Ah, I see what you mean now. >>> In that case, nevermind. I understand now why my idea is backwards incompatible. >>> What a shame. >>> Acknowledging that this idea no longer has ground to stand on, let me ask the >>> hypothetical -- would do-while loops be better with this feature implemented? I >>> am not at all trying to get this feature implemented anymore. I just want to >>> know if it would have been a good idea in the first place, backwards >>> compatibility issues aside. >>> On Mon, Jan 15, 2024 at 4:04 AM Tagir Valeev < [ mailto:amaembo at gmail.com | >>> amaembo at gmail.com ] > wrote: >>>> E.g.: >>>> public class DoWhile { >>>> static boolean b = false ; >>>> public static void main ( String [] args ) { >>>> do { >>>> boolean b = true ; >>>> } while ( b ); >>>> } >>>> } >>>> Now, this code is correct and finishes successfully. If we expand the scope of >>>> local b to the condition, it will shadow the field b. As a result, the code >>>> will still be correct but stuck in endless loop. >>>> With best regards, >>>> Tagir Valeev. >>>> On Mon, Jan 15, 2024 at 9:00 AM David Alayachew < [ >>>> mailto:davidalayachew at gmail.com | davidalayachew at gmail.com ] > wrote: >>>>> Hello, >>>>> Thank you for your response! >>>>> I am afraid I do not follow. Could you post an example? >>>>> From my quick experimentation now, it seems like what you describe isn't >>>>> possible. >>>>> The only way I could see what you are saying to be true is if a variable is in >>>>> scope for the while condition, but not in scope for the do-while body. And I >>>>> can't seem to create a situation where a variable is in scope for the while >>>>> condition, but is not in scope for the body. >>>>> Or maybe it is something else entirely? Please lmk. >>>>> Thank you for your time and help! >>>>> David Alayachew >>>>> On Mon, Jan 15, 2024 at 2:51 AM Tagir Valeev < [ mailto:amaembo at gmail.com | >>>>> amaembo at gmail.com ] > wrote: >>>>>> Note that this is not the compatible change, as the variable may shadow >>>>>> same-named field which was in the scope of condition before. >>>>>> On Mon, Jan 15, 2024, 08:49 David Alayachew < [ mailto:davidalayachew at gmail.com >>>>>> | davidalayachew at gmail.com ] > wrote: >>>>>>> Hello Amber Dev Team, >>>>>>> Would it make sense and would there be value in allowing variables declared in >>>>>>> the body of a do-while loop to still be considered in scope for the condition >>>>>>> of the same do-while loop? >>>>>>> Basically, the following example is currently not valid Java, but should it be? >>>>>>> ```java >>>>>>> do >>>>>>> { >>>>>>> final var someVar = someMethod(); >>>>>>> doSomethingWith(someVar); >>>>>>> //etc. >>>>>>> } >>>>>>> while (someVar.someBooleanReturningMethod()); >>>>>>> ``` >>>>>>> I think this is clear, simple, and is better than the alternative. >>>>>>> ```java >>>>>>> /* Must be mutable! :( */ >>>>>>> /* And you probably need a default value too! */ >>>>>>> var someVar = someYuckyDefaultOrWorseYetNull; >>>>>>> do >>>>>>> { >>>>>>> //code >>>>>>> } >>>>>>> while (someVar != 42); >>>>>>> //And annoyingly enough, the variable is still in scope outside of the loop! >>>>>>> Blegh. >>>>>>> ``` >>>>>>> Since do-while loops are very old, I wouldn't be surprised if someone else has >>>>>>> asked this before. But regardless, I couldn't find anything, so I ask now -- is >>>>>>> there any value in doing this? >>>>>>> Thank you for your time and help! >>>>>>> David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jan 15 17:34:28 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 15 Jan 2024 17:34:28 +0000 Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> Message-ID: Ok, the numbers in your benchmark match my expectation. So, if that approach doesn't match unsafe performance in the 1brc challenge (or come very close to it), I'm afraid the culprit is not the bound checks, as much as the time it takes for the var handle machinery to warm up (inline, unroll and drop the checks). We're aware of the startup/warmup advantage of Unsafe vs. FFM and we will be doing more in order to bridge the gap (a similar argument holds for JNI calls vs. FFM linker calls). Maurizio On 15/01/2024 17:05, Qu?n Anh Mai wrote: > Sure, I just thought that looking at the instruction count would be > more helpful, since each machine would express different performance > behaviours. For example, my machine shows dependency bound going from > [2] to [1] below, which leads to a much smaller margin of execution > time compared to the margin measured by other machines (such as the > test machine). The third implementation is similar to the first one, > except I use safe accesses in the form of bounded memory segment > accesses and varhandles. > > The JMH numbers for these versions look like this, I define an execute > function which is: > > ? ? @Benchmark > ? ? public PoorManMap execute() throws IOException { > ? ? ? ? try (var file = FileChannel.open(Path.of(FILE), > StandardOpenOption.READ); > ? ? ? ? ? ? ?var arena = Arena.ofShared()) { > ? ? ? ? ? ? var data = file.map(MapMode.READ_ONLY, 0, file.size(), arena); > ? ? ? ? ? ? return processFile(data, 0, data.byteSize()); > ? ? ? ? } > ? ? } > > ? ? CalculateAverage_merykitty.execute ? ? ?avgt ? ?5 ?7.422 ? 0.093 > ?ms/op // unsafe [1] > ? ? CalculateAverage_merykitty.execute ? ? ?avgt ? ?5 ?7.686 ? 0.181 > ?ms/op // universe segment [2] > ? ? CalculateAverage_merykitty.execute ? ? ?avgt ? ?5 ?9.009 ? 0.058 > ?ms/op // varhandle [3] > > [1]: https://github.com/merykitty/1brc/tree/main > > [2]: https://github.com/merykitty/1brc/tree/removeunsafe > > [3]: https://github.com/merykitty/1brc/tree/varhandles > > > Best regards, > Quan Anh > > On Tue, 16 Jan 2024 at 00:29, Maurizio Cimadamore > wrote: > > > On 15/01/2024 15:44, Qu?n Anh Mai wrote: >> Running the same program on 1e6 lines results in only 9e9 >> instructions, so I think the vast majority of the instruction >> count is of the compiled code. Not using the universe segment is >> roughly equivalent to my previous version, which would result in >> around 50% more instructions compared to using one, and almost >> double the instruction count of using Unsafe. > > Without looking at the program some more, it's hard for me to make > some sense of these numbers. I'm surprised that you don't see any > difference when using unbounded segment compared to regular ones. > I wonder if the gap you are seeing is due to the JVM warming up, > rather than peak performances being worse. Have you tried > measuring peak performance with e.g. JMH? I would not expect to > see 20% difference there... > > Maurizio > >> >> Regards, >> Quan Anh >> >> On Mon, 15 Jan 2024 at 23:09, Maurizio Cimadamore >> wrote: >> >> I think the increased instruction count is normal, as C2 had >> to do more work to optimize the bound checks away? >> >> Is there any difference compared to the version that doesn't >> use the universe segment? >> >> Maurizio >> >> On 15/01/2024 13:52, Qu?n Anh Mai wrote: >>> Hi, >>> >>> I have tried using a universe segment instead of Unsafe, and >>> store the custom hashmap buffer in off-heap instead of using >>> a byte array. The output of perf stat on the program >>> >>> ?Performance counter stats for 'sh >>> calculate_average_merykittyunsafe.sh': >>> >>> ? ? ? ? ? 13573.70 msec task-clock:u ?# ? 10.942 CPUs utilized >>> ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ?# ? ?0.000 /sec >>> ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ?# ? ?0.000 /sec >>> ? ? ? ? ? ? 238460 ? ? ?page-faults:u ? # ? 17.568 K/sec >>> ? ? ? ?61995179870 ? ? ?cycles:u ?# ? ?4.567 GHz >>> ? ? ? ? ?261830581 ?stalled-cycles-frontend:u # ? ?0.42% >>> frontend cycles idle >>> ? ? ? ? ? 93823680 ? ? ?stalled-cycles-backend:u ?# ? ?0.15% >>> backend cycles idle >>> ? ? ? 137976098809 ? ? ?instructions:u ?# ? ?2.23 ?insn per >>> cycle >>> ? # ? ?0.00 ?stalled cycles per insn >>> ? ? ? ?18373313803 ? ? ?branches:u ?# ? ?1.354 G/sec >>> ? ? ? ? ? 43579782 ? ? ?branch-misses:u ? # ? ?0.24% of all >>> branches >>> >>> ? ? ? ?1.240504612 seconds time elapsed >>> >>> ? ? ? 12.841563000 seconds user >>> ? ? ? ?0.652428000 seconds sys >>> >>> For comparison, this is the unsafe version: >>> >>> ?Performance counter stats for 'sh >>> calculate_average_merykittyunsafe.sh': >>> >>> ? ? ? ? ? 13327.46 msec task-clock:u ? ? ?# ? 11.202 CPUs >>> utilized >>> ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ? ? ?# ? ?0.000 /sec >>> ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ? ? ?# ? ?0.000 /sec >>> ? ? ? ? ? ? 269896 ? ? ?page-faults:u ? ? ? # ? 20.251 K/sec >>> ? ? ? ?61258348752 ? ? ?cycles:u ? ? ?# ? ?4.596 GHz >>> ? ? ? ? ?639839262 ?stalled-cycles-frontend:u # ? ?1.04% >>> frontend cycles idle >>> ? ? ? ? ?108018676 ?stalled-cycles-backend:u ?# ? ?0.18% >>> backend cycles idle >>> ? ? ? 113476168983 ? ? ?instructions:u ? ? ?# ? ?1.85 ?insn >>> per cycle >>> ? ? ? # ? ?0.01 ?stalled cycles per insn >>> ? ? ? ?11442665370 ? ? ?branches:u ? ? ?# ?858.578 M/sec >>> ? ? ? ? ? 44590172 ? ? ?branch-misses:u ? ? ? # ? ?0.39% of >>> all branches >>> >>> ? ? ? ?1.189768677 seconds time elapsed >>> >>> ? ? ? 12.628512000 seconds user >>> ? ? ? ?0.620083000 seconds sys >>> >>> This program running on my machine expresses dependency >>> bound so the difference in execution time is not as >>> significant as on the test machine but it can be seen that >>> removing Unsafe results in over 21% increase in instruction >>> count. >>> >>> Regards, >>> Quan Anh >>> >>> On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore >>> wrote: >>> >>> >>> On 12/01/2024 17:26, Qu?n Anh Mai wrote: >>> > FYI, in my submission to 1brc, using Unsafe decreases >>> the execution >>> > time from 3.25s to 2.57s on the test machine. >>> >>> Just curious - what is the difference compared with the >>> everything >>> segment trick? >>> >>> (While I know it can't do on-heap access, perhaps you >>> can tweak the code >>> to be all off-heap?) >>> >>> Maurizio >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jan 15 17:39:22 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 15 Jan 2024 17:39:22 +0000 Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> Message-ID: <2245319d-c706-4406-b3be-54a6b68363c2@oracle.com> By the way, I don't exactly know what you mean when you say that your machine shows/expresses dependency bound :-) Maurizio On 15/01/2024 17:05, Qu?n Anh Mai wrote: > Sure, I just thought that looking at the instruction count would be > more helpful, since each machine would express different performance > behaviours. For example, my machine shows dependency bound going from > [2] to [1] below, which leads to a much smaller margin of execution > time compared to the margin measured by other machines (such as the > test machine). The third implementation is similar to the first one, > except I use safe accesses in the form of bounded memory segment > accesses and varhandles. > > The JMH numbers for these versions look like this, I define an execute > function which is: > > ? ? @Benchmark > ? ? public PoorManMap execute() throws IOException { > ? ? ? ? try (var file = FileChannel.open(Path.of(FILE), > StandardOpenOption.READ); > ? ? ? ? ? ? ?var arena = Arena.ofShared()) { > ? ? ? ? ? ? var data = file.map(MapMode.READ_ONLY, 0, file.size(), arena); > ? ? ? ? ? ? return processFile(data, 0, data.byteSize()); > ? ? ? ? } > ? ? } > > ? ? CalculateAverage_merykitty.execute ? ? ?avgt ? ?5 ?7.422 ? 0.093 > ?ms/op // unsafe [1] > ? ? CalculateAverage_merykitty.execute ? ? ?avgt ? ?5 ?7.686 ? 0.181 > ?ms/op // universe segment [2] > ? ? CalculateAverage_merykitty.execute ? ? ?avgt ? ?5 ?9.009 ? 0.058 > ?ms/op // varhandle [3] > > [1]: https://github.com/merykitty/1brc/tree/main > > [2]: https://github.com/merykitty/1brc/tree/removeunsafe > > [3]: https://github.com/merykitty/1brc/tree/varhandles > > > Best regards, > Quan Anh > > On Tue, 16 Jan 2024 at 00:29, Maurizio Cimadamore > wrote: > > > On 15/01/2024 15:44, Qu?n Anh Mai wrote: >> Running the same program on 1e6 lines results in only 9e9 >> instructions, so I think the vast majority of the instruction >> count is of the compiled code. Not using the universe segment is >> roughly equivalent to my previous version, which would result in >> around 50% more instructions compared to using one, and almost >> double the instruction count of using Unsafe. > > Without looking at the program some more, it's hard for me to make > some sense of these numbers. I'm surprised that you don't see any > difference when using unbounded segment compared to regular ones. > I wonder if the gap you are seeing is due to the JVM warming up, > rather than peak performances being worse. Have you tried > measuring peak performance with e.g. JMH? I would not expect to > see 20% difference there... > > Maurizio > >> >> Regards, >> Quan Anh >> >> On Mon, 15 Jan 2024 at 23:09, Maurizio Cimadamore >> wrote: >> >> I think the increased instruction count is normal, as C2 had >> to do more work to optimize the bound checks away? >> >> Is there any difference compared to the version that doesn't >> use the universe segment? >> >> Maurizio >> >> On 15/01/2024 13:52, Qu?n Anh Mai wrote: >>> Hi, >>> >>> I have tried using a universe segment instead of Unsafe, and >>> store the custom hashmap buffer in off-heap instead of using >>> a byte array. The output of perf stat on the program >>> >>> ?Performance counter stats for 'sh >>> calculate_average_merykittyunsafe.sh': >>> >>> ? ? ? ? ? 13573.70 msec task-clock:u ?# ? 10.942 CPUs utilized >>> ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ?# ? ?0.000 /sec >>> ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ?# ? ?0.000 /sec >>> ? ? ? ? ? ? 238460 ? ? ?page-faults:u ? # ? 17.568 K/sec >>> ? ? ? ?61995179870 ? ? ?cycles:u ?# ? ?4.567 GHz >>> ? ? ? ? ?261830581 ?stalled-cycles-frontend:u # ? ?0.42% >>> frontend cycles idle >>> ? ? ? ? ? 93823680 ? ? ?stalled-cycles-backend:u ?# ? ?0.15% >>> backend cycles idle >>> ? ? ? 137976098809 ? ? ?instructions:u ?# ? ?2.23 ?insn per >>> cycle >>> ? # ? ?0.00 ?stalled cycles per insn >>> ? ? ? ?18373313803 ? ? ?branches:u ?# ? ?1.354 G/sec >>> ? ? ? ? ? 43579782 ? ? ?branch-misses:u ? # ? ?0.24% of all >>> branches >>> >>> ? ? ? ?1.240504612 seconds time elapsed >>> >>> ? ? ? 12.841563000 seconds user >>> ? ? ? ?0.652428000 seconds sys >>> >>> For comparison, this is the unsafe version: >>> >>> ?Performance counter stats for 'sh >>> calculate_average_merykittyunsafe.sh': >>> >>> ? ? ? ? ? 13327.46 msec task-clock:u ? ? ?# ? 11.202 CPUs >>> utilized >>> ? ? ? ? ? ? ? ? ?0 ? ? ?context-switches:u ? ? ?# ? ?0.000 /sec >>> ? ? ? ? ? ? ? ? ?0 ? ? ?cpu-migrations:u ? ? ?# ? ?0.000 /sec >>> ? ? ? ? ? ? 269896 ? ? ?page-faults:u ? ? ? # ? 20.251 K/sec >>> ? ? ? ?61258348752 ? ? ?cycles:u ? ? ?# ? ?4.596 GHz >>> ? ? ? ? ?639839262 ?stalled-cycles-frontend:u # ? ?1.04% >>> frontend cycles idle >>> ? ? ? ? ?108018676 ?stalled-cycles-backend:u ?# ? ?0.18% >>> backend cycles idle >>> ? ? ? 113476168983 ? ? ?instructions:u ? ? ?# ? ?1.85 ?insn >>> per cycle >>> ? ? ? # ? ?0.01 ?stalled cycles per insn >>> ? ? ? ?11442665370 ? ? ?branches:u ? ? ?# ?858.578 M/sec >>> ? ? ? ? ? 44590172 ? ? ?branch-misses:u ? ? ? # ? ?0.39% of >>> all branches >>> >>> ? ? ? ?1.189768677 seconds time elapsed >>> >>> ? ? ? 12.628512000 seconds user >>> ? ? ? ?0.620083000 seconds sys >>> >>> This program running on my machine expresses dependency >>> bound so the difference in execution time is not as >>> significant as on the test machine but it can be seen that >>> removing Unsafe results in over 21% increase in instruction >>> count. >>> >>> Regards, >>> Quan Anh >>> >>> On Sat, 13 Jan 2024 at 01:29, Maurizio Cimadamore >>> wrote: >>> >>> >>> On 12/01/2024 17:26, Qu?n Anh Mai wrote: >>> > FYI, in my submission to 1brc, using Unsafe decreases >>> the execution >>> > time from 3.25s to 2.57s on the test machine. >>> >>> Just curious - what is the difference compared with the >>> everything >>> segment trick? >>> >>> (While I know it can't do on-heap access, perhaps you >>> can tweak the code >>> to be all off-heap?) >>> >>> Maurizio >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From anhmdq at gmail.com Mon Jan 15 18:11:12 2024 From: anhmdq at gmail.com (=?UTF-8?Q?Qu=C3=A2n_Anh_Mai?=) Date: Tue, 16 Jan 2024 02:11:12 +0800 Subject: Official support for Unsafe In-Reply-To: <2245319d-c706-4406-b3be-54a6b68363c2@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> <2245319d-c706-4406-b3be-54a6b68363c2@oracle.com> Message-ID: Roughly speaking, throughput is determined by how many instructions a CPU can execute at the same time, and latency is determined by how many cycles it takes from when an instruction is executed to when its results are available. Consider this sequence: x1 = a1 + b1 x2 = a2 + b2 x3 = a3 + b3 x4 = a4 + b4 Since all these instructions are independent, all 4 of these can be issued at the same time, which leads to quadrupling the throughput of the whole sequence. At this point, the limiting factor is the number of execution units that are capable of executing adds. On the other hand, if we consider this sequence: x1 = x0 + a1 x2 = x1 + a2 x3 = x2 + a3 x4 = x3 + a4 The second instruction depends on the result of the first, and the third depends on the sequence. In this case, the CPU cannot execute them in parallel and must do so sequentially. As a result, the limiting factor is the overall latency of the instruction sequence. In general, a bound check does not create any additional dependency chain (only the control flow depends on it, but the branch predictor will take the right path way before that), which means that it will not impose any additional pressure if the program is bounded by the dependencies of the variables. On the other hand, a bound check consumes the execution ports, which means that the performance can degrade massively if the program is bounded by the execution throughput. In this example, when moving from VarHandle to using Universe segment, the program is bounded by the execution ports, which leads to a massive decrease in execution time (9ms -> 7.6ms), looking at perf stat the IPC stays pretty consistent at around 2.2 - 2.4. Removing more bound checks relieves the pressure on the execution ports and moves the bottleneck to the instruction latencies. As a result, although the instruction count decreases by about 20%, the execution time only is only reduced by around 5%. The IPC dips to around 1.8. Of course, this is machine-dependent, my machine is a Zen 4, which means that it is more likely to have higher overall throughput due to instructions generally improved to be able to run on more ports, while the shortest latency for an instruction is already 1 cycle. The turning point can be lower on other machines, which means that using Unsafe can be more advantageous in comparison to the universe segment approach. PS1: This is simplified, normally a program can have multiple parts that are bounded by different things ranging from the decoder, scheduler, etc, and bound checks will affect the performance when most of them are the main bottleneck. PS2: A bound check is also not cheap, it often requires a memory load, a compare and branch, and an arithmetic instruction when the types of the array and the access do not match. PS3: This is of course my speculation, which may be completely incorrect, so please take it with a grain of salt. Best regards, Quan Anh -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Jan 15 18:19:13 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 15 Jan 2024 13:19:13 -0500 Subject: New method on java.util.function.Function -- ternary method In-Reply-To: References: Message-ID: Hello Magnus, Thank you for closing my bug! Terribly sorry to ask another favor, but could you link the following link for traceability in the JBS submission? https://mail.openjdk.org/pipermail/amber-dev/2024-January/008488.html Thank you again for the time and help! David Alayachew On Mon, Jan 1, 2024 at 1:26?AM David Alayachew wrote: > Hello all, > > After reading through Brian Goetz's "Effect cases on Switch" > (specifically, the "Other switch tricks" section), I have decided to not > implement this feature after all. The fact is, switch expressions, > especially with "when-clauses", does a better job of clarifying intent than > this feature does. Sure, there is slightly more verbosity, but the intent > of this feature was never to minimize the character count, it was to > increase clarity. And as is, switch expressions with when clauses > accomplish that better. > > Here is a link to the subsection of the abovementioned article -- > https://inside.java/2023/12/15/switch-case-effect/#other-switch-tricks > > Could someone help me close this on JBS? Or should it just not be closed > at all? > > Here is a link to the JBS -- https://bugs.openjdk.org/browse/JDK-8319802 > > Thank you for your time and help! > David Alayachew > > On Thu, Nov 9, 2023 at 1:19?AM David Alayachew > wrote: > >> Oh, I will also add a specialized version of this method to >> java.util.function.UnaryOperator. That is because UnaryOperator is a >> subinterface of Function. Function goes T -> R, but UnaryOperator just goes >> T -> T. We do not want UnaryOperator to have the T -> R static function >> included due to inheritance, so it will get its own version that has the >> same method name and parameters, but the type parameters will be different. >> >> Here is another mockup - this time for UnaryOperator2's version of the >> same method. >> >> > interface UnaryOperator2 extends Function2, UnaryOperator >> > { >> > >> > public static UnaryOperator2 ternaryApply >> > ( >> > Predicate test, >> > Function trueFunction, >> > Function falseFunction >> > ) >> > { >> > >> > return >> > (T input) -> >> > test.test(input) >> > ? trueFunction.apply(input) >> > : falseFunction.apply(input) >> > ; >> > >> > } >> > >> > } >> >> On Thu, Nov 9, 2023 at 12:12?AM David Alayachew >> wrote: >> >>> It has been a month since I sent this proposal. Since no one has told me >>> that this is a terrible idea, I will submit this as an enhancement to JBS, >>> and once the ticket is made, start work on creating a pull request. >>> >>> On Tue, Oct 3, 2023 at 3:13?AM David Alayachew >>> wrote: >>> >>>> Whoops, bad import. >>>> >>>> Please replace the following line with the one after it. >>>> >>>> > import java.util.function.Function; >>>> >>>> > import java.util.function.*; >>>> >>>> On Tue, Oct 3, 2023 at 3:09?AM David Alayachew < >>>> davidalayachew at gmail.com> wrote: >>>> >>>>> Hello all, >>>>> >>>>> I have an idea that I want to run by you all -- a new method on >>>>> java.util.function.Function to capture ternary operations. >>>>> >>>>> Here is a mockup of what I wanted to do. >>>>> >>>>> > >>>>> > import java.util.function.Function; >>>>> > >>>>> > @FunctionalInterface >>>>> > public interface Function2 extends Function >>>>> > { >>>>> > >>>>> > public static Function ternary >>>>> > ( >>>>> > Predicate test, >>>>> > Function trueOutput, >>>>> > Function falseOutput >>>>> > ) >>>>> > { >>>>> > >>>>> > return >>>>> > (I input) -> >>>>> > test.test(input) >>>>> > ? trueOutput.apply(input) >>>>> > : falseOutput.apply(input) >>>>> > ; >>>>> > >>>>> > } >>>>> > >>>>> > } >>>>> > >>>>> >>>>> I think this is useful for a few reasons. >>>>> >>>>> * This composes, just like the ternary operator itself. >>>>> >>>>> * It pairs well with Function.identity() and method references to >>>>> clearly (but concisely) communicate intent. >>>>> >>>>> * Ternary operations are common, so this will find great use by >>>>> developers of all sorts. >>>>> >>>>> There is at least one part I don't quite like about this design - what >>>>> if one (or both!) of your outputs is not a functional transformation of the >>>>> input? >>>>> >>>>> For example, String username = id.isBlank() ? "UNKNOWN" : lookup(id); >>>>> >>>>> Obviously, this is easy to work around - simply ignore the input of >>>>> the function. But you lose clarity and simplicity that way. I would put a >>>>> note in the javadoc that says that this method should only be used in >>>>> instances where both outputs are a functional transformation of the input. >>>>> That way, intent is clarified. But if we go that route, maybe this function >>>>> should get a better name to capture that? testThenApply? ternaryTransform? >>>>> ternaryApply? >>>>> >>>>> Thank you for your time and help! >>>>> David Alayachew >>>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Mon Jan 15 18:23:04 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 15 Jan 2024 18:23:04 +0000 Subject: Official support for Unsafe In-Reply-To: References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> <2245319d-c706-4406-b3be-54a6b68363c2@oracle.com> Message-ID: <66e81e37-8855-44db-8471-a1c60ab5ee1d@oracle.com> Thanks for taking the time to explain. I know these concepts, I just needed to map them to the terminology you were using. But, I have questions: you say that bound check does not add dependencies, but consumes execution ports (thanks to branch predictor getting it right most of the time). If that was the case, we should see no difference between original version and everything segment version, given both are bounded by execution ports, and not by dependencies. So, why the massive decrease in execution time? (given instruction count didn't change that much?) I'm not necessarily saying your explanation is wrong, but it doesn't seem to account for the difference Maurizio On 15/01/2024 18:11, Qu?n Anh Mai wrote: > Roughly speaking, throughput is determined by how many instructions a > CPU can execute at the same time, and latency is determined by how > many cycles it takes from when an instruction is executed to when its > results are available. > > Consider this sequence: > > x1 = a1?+ b1 > x2 = a2?+ b2 > x3 = a3?+ b3 > x4 = a4?+ b4 > > Since all these instructions are independent, all 4 of these can be > issued at the same time, which leads to quadrupling the throughput of > the whole sequence. At this point, the limiting factor is the number > of execution units that are capable of executing adds. > > On the other hand, if we consider this sequence: > > x1 = x0 + a1 > x2 = x1?+ a2 > x3 = x2?+ a3 > x4 = x3?+ a4 > > The second instruction depends on the result of the first, and the > third depends on the sequence. In this case, the CPU cannot execute > them in parallel and must do so sequentially. As a result, the > limiting factor is the overall latency of the instruction sequence. > > In general, a bound check does not create any additional dependency > chain (only the control flow depends on it, but the branch > predictor?will take the right path way before that), which means that > it will not impose any additional pressure if the program is bounded > by the dependencies of the variables. On the other hand, a bound check > consumes the execution ports, which means that the performance can > degrade massively if the program is bounded by the execution throughput. > > In this example, when moving from VarHandle to using Universe segment, > the program is bounded by the execution ports, which leads to a > massive decrease in execution time (9ms -> 7.6ms), looking at perf > stat the IPC stays pretty consistent at around 2.2 - 2.4. Removing > more bound checks relieves the pressure on the execution ports and > moves the bottleneck to the instruction latencies. As a result, > although the instruction count decreases by about 20%, the execution > time only is only reduced by around 5%. The IPC dips to around 1.8. > > Of course, this is machine-dependent, my machine is a Zen 4, which > means that it is more likely to have higher overall throughput due to > instructions generally improved to be able to run on more ports, while > the shortest latency for an instruction is already 1 cycle. The > turning point can be lower on other machines, which means that using > Unsafe can be more advantageous in comparison to the universe segment > approach. > > PS1: This is simplified, normally a program can have multiple parts > that are bounded by different things ranging from the decoder, > scheduler, etc, and bound checks will affect the performance when most > of them are the main bottleneck. > PS2: A bound check is also not cheap, it often requires a memory load, > a compare and branch, and an arithmetic instruction when the types of > the array and the access do not match. > PS3: This is of course my speculation, which may be completely > incorrect, so please take it with a grain of salt. > > Best regards, > Quan Anh From maurizio.cimadamore at oracle.com Mon Jan 15 18:24:33 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 15 Jan 2024 18:24:33 +0000 Subject: Official support for Unsafe In-Reply-To: <66e81e37-8855-44db-8471-a1c60ab5ee1d@oracle.com> References: <9d9a0db2-cb15-481f-b749-7856f2e08b18@oracle.com> <79cb3216-20c1-438c-8556-d0373dd17d82@oracle.com> <43ce8cfc-724e-424b-91c0-ecf1fbf4f29b@oracle.com> <425cbbe8-4320-49b5-b3a2-ab080eb65e00@oracle.com> <59c2a096-43a4-4505-a8a3-1af35040dc93@oracle.com> <2245319d-c706-4406-b3be-54a6b68363c2@oracle.com> <66e81e37-8855-44db-8471-a1c60ab5ee1d@oracle.com> Message-ID: <8fd73ff6-7ad8-44d8-9243-2c11a8ae2206@oracle.com> Whoops - I did not realize this was still on amber-dev. I suggest we take this offline, as I don't think this discussion has any relationship with Project Amber. Maurizio On 15/01/2024 18:23, Maurizio Cimadamore wrote: > Thanks for taking the time to explain. I know these concepts, I just > needed to map them to the terminology you were using. > > But, I have questions: you say that bound check does not add > dependencies, but consumes execution ports (thanks to branch predictor > getting it right most of the time). > > If that was the case, we should see no difference between original > version and everything segment version, given both are bounded by > execution ports, and not by dependencies. > > So, why the massive decrease in execution time? (given instruction > count didn't change that much?) > > I'm not necessarily saying your explanation is wrong, but it doesn't > seem to account for the difference > > Maurizio > > On 15/01/2024 18:11, Qu?n Anh Mai wrote: >> Roughly speaking, throughput is determined by how many instructions a >> CPU can execute at the same time, and latency is determined by how >> many cycles it takes from when an instruction is executed to when its >> results are available. >> >> Consider this sequence: >> >> x1 = a1?+ b1 >> x2 = a2?+ b2 >> x3 = a3?+ b3 >> x4 = a4?+ b4 >> >> Since all these instructions are independent, all 4 of these can be >> issued at the same time, which leads to quadrupling the throughput of >> the whole sequence. At this point, the limiting factor is the number >> of execution units that are capable of executing adds. >> >> On the other hand, if we consider this sequence: >> >> x1 = x0 + a1 >> x2 = x1?+ a2 >> x3 = x2?+ a3 >> x4 = x3?+ a4 >> >> The second instruction depends on the result of the first, and the >> third depends on the sequence. In this case, the CPU cannot execute >> them in parallel and must do so sequentially. As a result, the >> limiting factor is the overall latency of the instruction sequence. >> >> In general, a bound check does not create any additional dependency >> chain (only the control flow depends on it, but the branch >> predictor?will take the right path way before that), which means that >> it will not impose any additional pressure if the program is bounded >> by the dependencies of the variables. On the other hand, a bound >> check consumes the execution ports, which means that the performance >> can degrade massively if the program is bounded by the execution >> throughput. >> >> In this example, when moving from VarHandle to using Universe >> segment, the program is bounded by the execution ports, which leads >> to a massive decrease in execution time (9ms -> 7.6ms), looking at >> perf stat the IPC stays pretty consistent at around 2.2 - 2.4. >> Removing more bound checks relieves the pressure on the execution >> ports and moves the bottleneck to the instruction latencies. As a >> result, although the instruction count decreases by about 20%, the >> execution time only is only reduced by around 5%. The IPC dips to >> around 1.8. >> >> Of course, this is machine-dependent, my machine is a Zen 4, which >> means that it is more likely to have higher overall throughput due to >> instructions generally improved to be able to run on more ports, >> while the shortest latency for an instruction is already 1 cycle. The >> turning point can be lower on other machines, which means that using >> Unsafe can be more advantageous in comparison to the universe segment >> approach. >> >> PS1: This is simplified, normally a program can have multiple parts >> that are bounded by different things ranging from the decoder, >> scheduler, etc, and bound checks will affect the performance when >> most of them are the main bottleneck. >> PS2: A bound check is also not cheap, it often requires a memory >> load, a compare and branch, and an arithmetic instruction when the >> types of the array and the access do not match. >> PS3: This is of course my speculation, which may be completely >> incorrect, so please take it with a grain of salt. >> >> Best regards, >> Quan Anh From vemana.github at gmail.com Thu Jan 18 03:20:03 2024 From: vemana.github at gmail.com (Subra V) Date: Thu, 18 Jan 2024 08:50:03 +0530 Subject: Thoughts on opt-in mutability? Message-ID: Hello Project Amber team, What are the current thoughts on providing source level mechanisms for opting-in to mutability rather than the current opt-out? For example, a notion of immutable class (not a record) can obviate specifying "private final" for fields. Opt-in mutability would perhaps also jive with two recent themes in language evolution: 1. Functional style 2. More cognition, less ceremony (switch statement, pattern matching to name a few). If there's prior relevant material, I'd appreciate a pointer; Dr. Google hasn't uncovered anything of note. I realize that this question has red flags of "proposing a solution" to "my specific problem". So, let me clarify that (1) I only write because I believe there's a reasonable chance that opt-in mutability is of fairly broad interest. (2) I am not proposing obviating the need for 'private final' as a solution; instead, it is meant to be analogous to saying 'I want String interpolation', a 'solution' from which language designers carefully teased apart a general problem and solved it in the form of String Templates. --- I am certain language designers are aware of this (and plenty more), but in the interest of intellectual honesty, let me attempt to articulate problems with default-mutability from my default-immutable practice. 1. [Immutable style feels second class] Java has moved in a functional direction much to my (and most practitioner's?) pleasure. For the subset of folks who buy into this direction, Immutable classes are natural. Yet, they feel second class in that (1) notions of "private final" aren't relevant yet pollute (2) It requires extra work [making all fields final, binding them in constructor] compared to the default case of mutability 2. [Cognitive Cost of Ceremony] For non-record Immutable classes, I have to (1) Write 'private final X' and then add a constructor param and set `this.X = X` in the constructor. (2) Read and verify `private final` on every field to know that the class is immutable. Even though IDEs help, it breaks the flow of thought both when reading and writing 3. [Poor Prototyping Velocity] I often find the ceremony especially frustrating in early phases of design, especially when using immutable classes. Imagine I have 20 concepts/fields and I am attempting to organize them. A common occurrence is to realize 'ohh this field/method logically belongs to that other class; let me move it there' but it is a pretty big chore to do so (even with Intellij) given that immutability takes extra work to achieve. Such moves also come with further transitive ripple effects. All the busy work perhaps accounts for upwards of 30% of the initial few hours/days of design and more importantly, constantly interrupts thoughts. For contrast, if I just make every field public during the initial period, it'd probably be a better experience, but then I need a magic wand to make them all immutable after the design settles down. A one line distillation of the above is perhaps: "Immutability is common enough to consider a fast path at source level; the current slow path has negative consequences for cognition and devx." Appreciate thoughts. Thank you, Subrahmanyam -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jan 18 15:52:59 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 18 Jan 2024 10:52:59 -0500 Subject: Thoughts on opt-in mutability? In-Reply-To: References: Message-ID: So, you provide several good arguments here for "Java picked a bad default with respect to mutability."? (It is in good company; Java picked many bad defaults, such as package visibility rather than private, classes being extensible rather than closed, etc.)? Let's just take it as given that non-final is an imperfect default for fields. Which brings us to the next question: "now that we realize the default is bad, should we change it?"? This is a much harder question, because it involves the reality of 10M developers and billions of lines of code. One bit of prior art here that might be relevant (and the experiment is still playing out) is C#'s flipping of the nullability default on a per-module basis (with injecting checking at the boundaries.)? This was a bold experiment and we are interested in seeing the results. The problem with trying to fix the mistakes of the past is that in most cases, you are just creating a fork and forcing users to deal with both branches.? If we had a directive: ??? class X { ??????? #pragma default-final ??????? ... ??? } then now users have to keep track (possibly on a per-file basis) of where "int x" means final or mutable.? This is adding cognitive load to all users, since almost no Java developer has the luxury of controlling 100% of the code they deal with. > A one line distillation of the above is perhaps: "Immutability is > common enough to consider a fast path at source level; the current > slow path has negative consequences for cognition and devx." Valid, but what this misses is: "having to constantly reason about whether line #656 of Moo.java is on the fast path or the slow path has even higher cognitive load." On 1/17/2024 10:20 PM, Subra V wrote: > Hello Project Amber team, > > What are the current thoughts?on providing source level mechanisms for > opting-in to mutability rather than the current opt-out? For example, > a notion of immutable class (not a record) can?obviate specifying > "private final" for fields. Opt-in mutability would perhaps also jive > with two recent themes in language evolution: 1. Functional style 2. > More cognition, less ceremony (switch statement, pattern matching to > name a few). > > If there's prior relevant material, I'd appreciate?a pointer; Dr. > Google hasn't uncovered anything of note. > > I realize that this question has red flags of "proposing a solution" > to "my specific problem". So, let?me clarify that (1) I only write > because I believe there's a reasonable chance that opt-in mutability > is of fairly broad interest. (2) I am not proposing obviating the need > for 'private final' as a solution; instead, it is meant to be > analogous to saying 'I want String interpolation', a 'solution' from > which language designers carefully teased apart a general?problem and > solved it in the form of String Templates. > --- > > I am certain language designers are aware of this (and plenty more), > but in the interest of intellectual honesty, let me attempt to > articulate problems with default-mutability from my default-immutable > practice. > > 1. [Immutable style feels second class] Java has moved in a functional > direction much to my (and most practitioner's?) pleasure.?For > the?subset of folks who buy into this direction, Immutable classes are > natural. Yet, they feel?second class in that (1) notions of "private > final"?aren't relevant yet pollute?(2) It requires extra work [making > all fields final, binding them in constructor] compared to the default > case of mutability > > 2. [Cognitive Cost of Ceremony] For non-record Immutable classes, I > have to (1) Write 'private final X' and then add a constructor?param > and set `this.X = X` in the constructor. (2) Read and verify `private > final` on every field to know that the class is immutable. Even though > IDEs help, it breaks the flow of thought both when reading and writing > > 3. [Poor Prototyping Velocity] I often find the ceremony especially > frustrating in early phases of design, especially when using immutable > classes. Imagine I have 20 concepts/fields and I am attempting > to?organize them. A common occurrence is to realize 'ohh this > field/method logically belongs to that other class; let me move it > there' but it is a pretty big chore to do so (even with Intellij) > given that immutability takes extra work to achieve. Such moves also > come with further transitive ripple effects. All the busy work perhaps > accounts for upwards of 30% of the initial few hours/days of design > and more importantly, constantly interrupts thoughts. For contrast, if > I just make every field public during the initial period, it'd > probably be a better experience, but then I need a magic wand to make > them all immutable after the design settles down. > > A one line distillation of the above is perhaps: "Immutability is > common enough to consider a fast path at source level; the current > slow path has negative consequences for cognition and devx." > > Appreciate thoughts. > > Thank you, > Subrahmanyam > -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Thu Jan 18 18:43:48 2024 From: redio.development at gmail.com (Red IO) Date: Thu, 18 Jan 2024 19:43:48 +0100 Subject: Thoughts on opt-in mutability? In-Reply-To: References: Message-ID: One idea could be to make the class declaration special. In well structured classes all fields should live at the top so having a class modifier like "immutable" would be rather obvious even with lots of fields. Also rather a field declared at line 20 and used in line 650 is mutable already requires scrolling to the top. On that note the editor/ide is a more often used indicator for mutablity of fields by having color or styling differences in the variable name. (I know relying on tooling to make things clear is not preferable but it's kinda already this way) An example class would look similar to this : public immutable class Test { private int field1; private int field2; private int field3; private int field4; private int field5; private int field6; private int field7; private int field8; private mutable int field9; private mutable int field10; } Rather to combine default final and default private is another discussion. A counter argument would be that allowing for any combination of defaults would result in a keyword explosion: immutable mutable closed (wip to make a class private default) package-private Just to allow inverting the defaults. Also c# already has this for immutable fields. They have the readonly keyword which is equivalent to final on fields in java but it also works on the class making every field readonly. The problem with that approach in java is that we already used final on classes to block inheritance. Maybe we could instead drop the inversion (package-private and mutable) and make it a 1 way decision like in records. But then the question would be why not just use a record. That are my thoughts on this topic. As one might have heard out I'm undecided on the topic. Great regards RedIODev On Thu, Jan 18, 2024, 17:56 Brian Goetz wrote: > So, you provide several good arguments here for "Java picked a bad default > with respect to mutability." (It is in good company; Java picked many bad > defaults, such as package visibility rather than private, classes being > extensible rather than closed, etc.) Let's just take it as given that > non-final is an imperfect default for fields. > > Which brings us to the next question: "now that we realize the default is > bad, should we change it?" This is a much harder question, because it > involves the reality of 10M developers and billions of lines of code. > > One bit of prior art here that might be relevant (and the experiment is > still playing out) is C#'s flipping of the nullability default on a > per-module basis (with injecting checking at the boundaries.) This was a > bold experiment and we are interested in seeing the results. > > The problem with trying to fix the mistakes of the past is that in most > cases, you are just creating a fork and forcing users to deal with both > branches. If we had a directive: > > class X { > #pragma default-final > > ... > } > > then now users have to keep track (possibly on a per-file basis) of where > "int x" means final or mutable. This is adding cognitive load to all > users, since almost no Java developer has the luxury of controlling 100% of > the code they deal with. > > A one line distillation of the above is perhaps: "Immutability is common > enough to consider a fast path at source level; the current slow path has > negative consequences for cognition and devx." > > > Valid, but what this misses is: "having to constantly reason about whether > line #656 of Moo.java is on the fast path or the slow path has even higher > cognitive load." > > > On 1/17/2024 10:20 PM, Subra V wrote: > > Hello Project Amber team, > > What are the current thoughts on providing source level mechanisms for > opting-in to mutability rather than the current opt-out? For example, a > notion of immutable class (not a record) can obviate specifying "private > final" for fields. Opt-in mutability would perhaps also jive with two > recent themes in language evolution: 1. Functional style 2. More cognition, > less ceremony (switch statement, pattern matching to name a few). > > If there's prior relevant material, I'd appreciate a pointer; Dr. Google > hasn't uncovered anything of note. > > I realize that this question has red flags of "proposing a solution" to > "my specific problem". So, let me clarify that (1) I only write because I > believe there's a reasonable chance that opt-in mutability is of fairly > broad interest. (2) I am not proposing obviating the need for 'private > final' as a solution; instead, it is meant to be analogous to saying 'I > want String interpolation', a 'solution' from which language designers > carefully teased apart a general problem and solved it in the form of > String Templates. > --- > > I am certain language designers are aware of this (and plenty more), but > in the interest of intellectual honesty, let me attempt to articulate > problems with default-mutability from my default-immutable practice. > > 1. [Immutable style feels second class] Java has moved in a functional > direction much to my (and most practitioner's?) pleasure. For the subset of > folks who buy into this direction, Immutable classes are natural. Yet, they > feel second class in that (1) notions of "private final" aren't relevant > yet pollute (2) It requires extra work [making all fields final, binding > them in constructor] compared to the default case of mutability > > 2. [Cognitive Cost of Ceremony] For non-record Immutable classes, I have > to (1) Write 'private final X' and then add a constructor param and set > `this.X = X` in the constructor. (2) Read and verify `private final` on > every field to know that the class is immutable. Even though IDEs help, it > breaks the flow of thought both when reading and writing > > 3. [Poor Prototyping Velocity] I often find the ceremony especially > frustrating in early phases of design, especially when using immutable > classes. Imagine I have 20 concepts/fields and I am attempting to organize > them. A common occurrence is to realize 'ohh this field/method logically > belongs to that other class; let me move it there' but it is a pretty big > chore to do so (even with Intellij) given that immutability takes extra > work to achieve. Such moves also come with further transitive ripple > effects. All the busy work perhaps accounts for upwards of 30% of the > initial few hours/days of design and more importantly, constantly > interrupts thoughts. For contrast, if I just make every field public during > the initial period, it'd probably be a better experience, but then I need a > magic wand to make them all immutable after the design settles down. > > A one line distillation of the above is perhaps: "Immutability is common > enough to consider a fast path at source level; the current slow path has > negative consequences for cognition and devx." > > Appreciate thoughts. > > Thank you, > Subrahmanyam > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Thu Jan 18 19:02:01 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Thu, 18 Jan 2024 14:02:01 -0500 Subject: Thoughts on opt-in mutability? In-Reply-To: References: Message-ID: <59c827e7-d6ff-4b1c-b713-9c22d79b1ab1@oracle.com> I'll just add that none of this "adds" anything to the language (unlike records, or value types, or pattern matching); it just seeks to streamline the declaration of what the language already supports.? That makes it a pretty weak feature. People have argued in the past for having explicit versions of everything that is currently implicit (if you don't say "static" you get an instance method/field; if you don't say public/private/protected you get a package-private member).? For most of these, there's no way to explicitly say "instance" or "package-private". We could of course do this; we even reserved the `non-` keyword naming convention to reduce the bikeshed painting over what to call them.? But we never did any of these, because: we don't really think anyone is going to use them.? No one wants to say: ?? package-private class Foo { ?????? non-static non-final package-private int x; ?????? ... ?? } So such features are often just to satisfy an abstract sense of completion, which is to say, mostly useless.? (And to add insult to injury, the bikeshed quotient of such features is very high.) I'd rather spend our efforts on pattern matching and value types. On 1/18/2024 1:43 PM, Red IO wrote: > One idea could be to make the class declaration special. In well > structured classes all fields should live at the top so having a class > modifier like "immutable" would be rather obvious even with lots of > fields. Also rather a field declared at line 20 and used in line 650 > is mutable already requires scrolling to the top. > > On that note the editor/ide is a more often used indicator for > mutablity of fields by having color or styling differences in the > variable name. (I know relying on tooling to make things clear is not > preferable but it's kinda already this way) > > An example class would look similar to this : > > public immutable class Test { > private int field1; > private int field2; > private int field3; > private int field4; > private int field5; > private int field6; > private int field7; > private int field8; > private mutable int field9; > private mutable int field10; > } > > Rather to combine default final and default private is another > discussion. > > A counter argument would be that allowing for any combination of > defaults would result in a keyword explosion: > immutable > mutable > closed (wip to make a class private default) > package-private > > Just to allow inverting the defaults. > > Also c# already has this for immutable fields. They have the readonly > keyword which is equivalent to final on fields in java but it also > works on the class making every field readonly. The problem with that > approach in java is that we already used final on classes to block > inheritance. > > Maybe we could instead drop the inversion (package-private and > mutable) and make it a 1 way decision like in records. But then the > question would be why not just use a record. > > That are my thoughts on this topic. As one might have heard out I'm > undecided on the topic. > > Great regards > RedIODev > > On Thu, Jan 18, 2024, 17:56 Brian Goetz wrote: > > So, you provide several good arguments here for "Java picked a bad > default with respect to mutability."? (It is in good company; Java > picked many bad defaults, such as package visibility rather than > private, classes being extensible rather than closed, etc.)? Let's > just take it as given that non-final is an imperfect default for > fields. > > Which brings us to the next question: "now that we realize the > default is bad, should we change it?"? This is a much harder > question, because it involves the reality of 10M developers and > billions of lines of code. > > One bit of prior art here that might be relevant (and the > experiment is still playing out) is C#'s flipping of the > nullability default on a per-module basis (with injecting checking > at the boundaries.)? This was a bold experiment and we are > interested in seeing the results. > > The problem with trying to fix the mistakes of the past is that in > most cases, you are just creating a fork and forcing users to deal > with both branches.? If we had a directive: > > ??? class X { > ??????? #pragma default-final > > ??????? ... > ??? } > > then now users have to keep track (possibly on a per-file basis) > of where "int x" means final or mutable.? This is adding cognitive > load to all users, since almost no Java developer has the luxury > of controlling 100% of the code they deal with. > >> A one line distillation of the above is perhaps: "Immutability is >> common enough to consider a fast path at source level; the >> current slow path has negative consequences for cognition and devx." > > Valid, but what this misses is: "having to constantly reason about > whether line #656 of Moo.java is on the fast path or the slow path > has even higher cognitive load." > > > On 1/17/2024 10:20 PM, Subra V wrote: >> Hello Project Amber team, >> >> What are the current thoughts?on providing source level >> mechanisms for opting-in to mutability rather than the current >> opt-out? For example, a notion of immutable class (not a record) >> can?obviate specifying "private final" for fields. Opt-in >> mutability would perhaps also jive with two recent themes in >> language evolution: 1. Functional style 2. More cognition, less >> ceremony (switch statement, pattern matching to name a few). >> >> If there's prior relevant material, I'd appreciate?a pointer; Dr. >> Google hasn't uncovered anything of note. >> >> I realize that this question has red flags of "proposing a >> solution" to "my specific problem". So, let?me clarify that (1) I >> only write because I believe there's a reasonable chance that >> opt-in mutability is of fairly broad interest. (2) I am not >> proposing obviating the need for 'private final' as a solution; >> instead, it is meant to be analogous to saying 'I want String >> interpolation', a 'solution' from which language designers >> carefully teased apart a general?problem and solved it in the >> form of String Templates. >> --- >> >> I am certain language designers are aware of this (and plenty >> more), but in the interest of intellectual honesty, let me >> attempt to articulate problems with default-mutability from my >> default-immutable practice. >> >> 1. [Immutable style feels second class] Java has moved in a >> functional direction much to my (and most practitioner's?) >> pleasure.?For the?subset of folks who buy into this direction, >> Immutable classes are natural. Yet, they feel?second class in >> that (1) notions of "private final"?aren't relevant yet >> pollute?(2) It requires extra work [making all fields final, >> binding them in constructor] compared to the default case of >> mutability >> >> 2. [Cognitive Cost of Ceremony] For non-record Immutable classes, >> I have to (1) Write 'private final X' and then add a >> constructor?param and set `this.X = X` in the constructor. (2) >> Read and verify `private final` on every field to know that the >> class is immutable. Even though IDEs help, it breaks the flow of >> thought both when reading and writing >> >> 3. [Poor Prototyping Velocity] I often find the ceremony >> especially frustrating in early phases of design, especially when >> using immutable classes. Imagine I have 20 concepts/fields and I >> am attempting to?organize them. A common occurrence is to realize >> 'ohh this field/method logically belongs to that other class; let >> me move it there' but it is a pretty big chore to do so (even >> with Intellij) given that immutability takes extra work to >> achieve. Such moves also come with further transitive ripple >> effects. All the busy work perhaps accounts for upwards of 30% of >> the initial few hours/days of design and more importantly, >> constantly interrupts thoughts. For contrast, if I just make >> every field public during the initial period, it'd probably be a >> better experience, but then I need a magic wand to make them all >> immutable after the design settles down. >> >> A one line distillation of the above is perhaps: "Immutability is >> common enough to consider a fast path at source level; the >> current slow path has negative consequences for cognition and devx." >> >> Appreciate thoughts. >> >> Thank you, >> Subrahmanyam >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vemana.github at gmail.com Fri Jan 19 00:56:24 2024 From: vemana.github at gmail.com (Subra V) Date: Fri, 19 Jan 2024 06:26:24 +0530 Subject: Thoughts on opt-in mutability? In-Reply-To: <59c827e7-d6ff-4b1c-b713-9c22d79b1ab1@oracle.com> References: <59c827e7-d6ff-4b1c-b713-9c22d79b1ab1@oracle.com> Message-ID: Dr. Goetz, Thanks for the thoughts. My takeaway is that it is on the back burner waiting on potential triggers like the C# nullability experiment. That it is not being summarily dismissed is encouraging. I am content for it to remain on its current trajectory, but I wish to leave a few thoughts. I'll just add that none of this "adds" anything to the language (unlike > records, or value types, or pattern matching); it just seeks to streamline > the declaration of what the language already supports. That makes it a > pretty weak feature. To my 'language user' eyes, streamlining switch statement into switch expression was a strong feature despite the fact that it didn't add anything to the language (strictly speaking). I think of streamlined immutable classes in a similar way. I recognize that switch expressions got prioritized the way it did because it unlocked the bigger prize of pattern matching, while "streamlining immutability" doesn't have that big prize except perhaps a subset of thankful devs. But, even in isolation, switch expressions were a strong feature in their own right because of the improved cognition, something that "streamlined immutability" could possibly claim as well. I'd rather spend our efforts on pattern matching and value types. I think the overwhelming majority would agree: Value Types > Pattern matching > [Day light] > Streamlining immutability. Let me conclude with one aspect that I haven't seen discussed publicly. [Shifting bottlenecks] All of the Amber changes are producing strong network effects that are increasing (in fact, have increased) the set of programs for which Java is a very strong choice. In turn, the bottlenecks in producing such programs have also moved. For example, the other day I needed a command line utility to transform java source code (like inlining a class into another, renaming a class and such). I encoded the changes as a small language [example: RenameClass(NewMain, InlineClass(OldMain, Child1, Child2, Child3)) says: Inline the children classes into a parent class called OldMain and then rename it into NewMain] and wrote a quick parser using parser combinator style in Java. It was a pleasant experience except for the aforementioned [Poor Rapid Prototyping Velocity] in my first post - the bottleneck was fiddling with moving fields around and dealing with the verbosity and effort of immutability. Sealed types and pattern matching were relatively effortless. A decade ago, I wouldn't even have chosen Java; Visitors make my head spin. A few years ago (and perhaps still), I've seen Go recommended for command line apps, but I couldn't pull off Parser combinator style with Go effortlessly (lack of first class union types). In other words, Java has gone from Heck No --> Plausible --> even Pleasant in the space of a few Amber releases and the bottleneck in producing the program correspondingly moved from "Visitors" to "Second class Immutability". I know this is just one example, but it is a recurring theme in my practice. Like I said before, I am content for things to remain on their current trajectory - I trust the language designers' judgment calls - but I hope this anecdote helps in some little way. On Fri, Jan 19, 2024 at 12:32?AM Brian Goetz wrote: > I'll just add that none of this "adds" anything to the language (unlike > records, or value types, or pattern matching); it just seeks to streamline > the declaration of what the language already supports. That makes it a > pretty weak feature. > > People have argued in the past for having explicit versions of everything > that is currently implicit (if you don't say "static" you get an instance > method/field; if you don't say public/private/protected you get a > package-private member). For most of these, there's no way to explicitly > say "instance" or "package-private". > > We could of course do this; we even reserved the `non-` keyword naming > convention to reduce the bikeshed painting over what to call them. But we > never did any of these, because: we don't really think anyone is going to > use them. No one wants to say: > > package-private class Foo { > non-static non-final package-private int x; > ... > } > > So such features are often just to satisfy an abstract sense of > completion, which is to say, mostly useless. (And to add insult to injury, > the bikeshed quotient of such features is very high.) > > I'd rather spend our efforts on pattern matching and value types. > > > On 1/18/2024 1:43 PM, Red IO wrote: > > One idea could be to make the class declaration special. In well > structured classes all fields should live at the top so having a class > modifier like "immutable" would be rather obvious even with lots of fields. > Also rather a field declared at line 20 and used in line 650 is mutable > already requires scrolling to the top. > > On that note the editor/ide is a more often used indicator for mutablity > of fields by having color or styling differences in the variable name. (I > know relying on tooling to make things clear is not preferable but it's > kinda already this way) > > An example class would look similar to this : > > public immutable class Test { > private int field1; > private int field2; > private int field3; > private int field4; > private int field5; > private int field6; > private int field7; > private int field8; > private mutable int field9; > private mutable int field10; > } > > Rather to combine default final and default private is another discussion. > > A counter argument would be that allowing for any combination of defaults > would result in a keyword explosion: > immutable > mutable > closed (wip to make a class private default) > package-private > > Just to allow inverting the defaults. > > Also c# already has this for immutable fields. They have the readonly > keyword which is equivalent to final on fields in java but it also works on > the class making every field readonly. The problem with that approach in > java is that we already used final on classes to block inheritance. > > Maybe we could instead drop the inversion (package-private and mutable) > and make it a 1 way decision like in records. But then the question would > be why not just use a record. > > That are my thoughts on this topic. As one might have heard out I'm > undecided on the topic. > > Great regards > RedIODev > > On Thu, Jan 18, 2024, 17:56 Brian Goetz wrote: > >> So, you provide several good arguments here for "Java picked a bad >> default with respect to mutability." (It is in good company; Java picked >> many bad defaults, such as package visibility rather than private, classes >> being extensible rather than closed, etc.) Let's just take it as given >> that non-final is an imperfect default for fields. >> >> Which brings us to the next question: "now that we realize the default is >> bad, should we change it?" This is a much harder question, because it >> involves the reality of 10M developers and billions of lines of code. >> >> One bit of prior art here that might be relevant (and the experiment is >> still playing out) is C#'s flipping of the nullability default on a >> per-module basis (with injecting checking at the boundaries.) This was a >> bold experiment and we are interested in seeing the results. >> >> The problem with trying to fix the mistakes of the past is that in most >> cases, you are just creating a fork and forcing users to deal with both >> branches. If we had a directive: >> >> class X { >> #pragma default-final >> >> ... >> } >> >> then now users have to keep track (possibly on a per-file basis) of where >> "int x" means final or mutable. This is adding cognitive load to all >> users, since almost no Java developer has the luxury of controlling 100% of >> the code they deal with. >> >> A one line distillation of the above is perhaps: "Immutability is common >> enough to consider a fast path at source level; the current slow path has >> negative consequences for cognition and devx." >> >> >> Valid, but what this misses is: "having to constantly reason about >> whether line #656 of Moo.java is on the fast path or the slow path has even >> higher cognitive load." >> >> >> On 1/17/2024 10:20 PM, Subra V wrote: >> >> Hello Project Amber team, >> >> What are the current thoughts on providing source level mechanisms for >> opting-in to mutability rather than the current opt-out? For example, a >> notion of immutable class (not a record) can obviate specifying "private >> final" for fields. Opt-in mutability would perhaps also jive with two >> recent themes in language evolution: 1. Functional style 2. More cognition, >> less ceremony (switch statement, pattern matching to name a few). >> >> If there's prior relevant material, I'd appreciate a pointer; Dr. Google >> hasn't uncovered anything of note. >> >> I realize that this question has red flags of "proposing a solution" to >> "my specific problem". So, let me clarify that (1) I only write because I >> believe there's a reasonable chance that opt-in mutability is of fairly >> broad interest. (2) I am not proposing obviating the need for 'private >> final' as a solution; instead, it is meant to be analogous to saying 'I >> want String interpolation', a 'solution' from which language designers >> carefully teased apart a general problem and solved it in the form of >> String Templates. >> --- >> >> I am certain language designers are aware of this (and plenty more), but >> in the interest of intellectual honesty, let me attempt to articulate >> problems with default-mutability from my default-immutable practice. >> >> 1. [Immutable style feels second class] Java has moved in a functional >> direction much to my (and most practitioner's?) pleasure. For the subset of >> folks who buy into this direction, Immutable classes are natural. Yet, they >> feel second class in that (1) notions of "private final" aren't relevant >> yet pollute (2) It requires extra work [making all fields final, binding >> them in constructor] compared to the default case of mutability >> >> 2. [Cognitive Cost of Ceremony] For non-record Immutable classes, I have >> to (1) Write 'private final X' and then add a constructor param and set >> `this.X = X` in the constructor. (2) Read and verify `private final` on >> every field to know that the class is immutable. Even though IDEs help, it >> breaks the flow of thought both when reading and writing >> >> 3. [Poor Prototyping Velocity] I often find the ceremony especially >> frustrating in early phases of design, especially when using immutable >> classes. Imagine I have 20 concepts/fields and I am attempting to organize >> them. A common occurrence is to realize 'ohh this field/method logically >> belongs to that other class; let me move it there' but it is a pretty big >> chore to do so (even with Intellij) given that immutability takes extra >> work to achieve. Such moves also come with further transitive ripple >> effects. All the busy work perhaps accounts for upwards of 30% of the >> initial few hours/days of design and more importantly, constantly >> interrupts thoughts. For contrast, if I just make every field public during >> the initial period, it'd probably be a better experience, but then I need a >> magic wand to make them all immutable after the design settles down. >> >> A one line distillation of the above is perhaps: "Immutability is common >> enough to consider a fast path at source level; the current slow path has >> negative consequences for cognition and devx." >> >> Appreciate thoughts. >> >> Thank you, >> Subrahmanyam >> >> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Tue Jan 23 11:03:59 2024 From: duke at openjdk.org (duke) Date: Tue, 23 Jan 2024 11:03:59 GMT Subject: git: openjdk/amber-docs: Adding new note about member patterns by Brian Goetz Message-ID: <8e979430-9033-4404-87aa-18ff38c99561@openjdk.org> Changeset: d9b68660 Author: Gavin Bierman Date: 2024-01-23 11:03:12 +0000 URL: https://git.openjdk.org/amber-docs/commit/d9b68660f5d2beb25df6c8c4b7f266d56d352217 Adding new note about member patterns by Brian Goetz ! site/_index.md + site/design-notes/patterns/towards-member-patterns.md From duke at openjdk.org Tue Jan 23 11:40:53 2024 From: duke at openjdk.org (David Alayachew) Date: Tue, 23 Jan 2024 11:40:53 GMT Subject: [amber-docs] RFR: Update towards-member-patterns.md -- Fixing typos Message-ID: Fixing some typos. ------------- Commit messages: - Update towards-member-patterns.md -- Fixing typos Changes: https://git.openjdk.org/amber-docs/pull/22/files Webrev: https://webrevs.openjdk.org/?repo=amber-docs&pr=22&range=00 Stats: 3 lines in 1 file changed: 0 ins; 0 del; 3 mod Patch: https://git.openjdk.org/amber-docs/pull/22.diff Fetch: git fetch https://git.openjdk.org/amber-docs.git pull/22/head:pull/22 PR: https://git.openjdk.org/amber-docs/pull/22 From duke at openjdk.org Tue Jan 23 11:40:53 2024 From: duke at openjdk.org (David Alayachew) Date: Tue, 23 Jan 2024 11:40:53 GMT Subject: [amber-docs] RFR: Update towards-member-patterns.md -- Fixing typos In-Reply-To: References: Message-ID: On Tue, 23 Jan 2024 11:33:03 GMT, David Alayachew wrote: > Fixing some typos. site/design-notes/patterns/towards-member-patterns.md line 495: > 493: where `String::instanceof` means the same as the previous lambda example. This > 494: means that APIs like `Stream` can abstract over conditional behavior as well as > 495: unconditional. Whoops, I did not mean to make this change. Lmk if you all want me to fix this, but since it is Markdown, it should not affect the output at all. ------------- PR Review Comment: https://git.openjdk.org/amber-docs/pull/22#discussion_r1463139445 From duke at openjdk.org Tue Jan 23 16:52:50 2024 From: duke at openjdk.org (David Alayachew) Date: Tue, 23 Jan 2024 16:52:50 GMT Subject: [amber-docs] Integrated: Update towards-member-patterns.md -- Fixing typos In-Reply-To: References: Message-ID: On Tue, 23 Jan 2024 11:33:03 GMT, David Alayachew wrote: > Fixing some typos. This pull request has now been integrated. Changeset: c13d25d6 Author: David Alayachew Committer: Gavin Bierman URL: https://git.openjdk.org/amber-docs/commit/c13d25d61b612f6690ed1727e7d0da78c6f65152 Stats: 3 lines in 1 file changed: 0 ins; 0 del; 3 mod Update towards-member-patterns.md -- Fixing typos ------------- PR: https://git.openjdk.org/amber-docs/pull/22 From tanksherman27 at gmail.com Thu Jan 25 13:18:00 2024 From: tanksherman27 at gmail.com (Julian Waters) Date: Thu, 25 Jan 2024 21:18:00 +0800 Subject: Non final records Message-ID: Hi all, Just a quick question: Why isn't modifying the contents of a record supported formally? (Without reflection and the like) Wouldn't it be helpful to be able to modify a record's fields? Is there any plan to allow doing so in the future? best regards, Julian From maurizio.cimadamore at oracle.com Thu Jan 25 13:26:09 2024 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 25 Jan 2024 13:26:09 +0000 Subject: Non final records In-Reply-To: References: Message-ID: Hi Julian, a record is just a wrapper around some data, which exposes nice semantics properties? - for instance, given a record Point(int x, int y), it is always the case that: ``` Point p = new Point(1, 2); assertEquals(new Point(p.x(), p.y()), p); ``` (this is actually the basis of why derived creation can be expressed). In other words, records are meant to be stateless beasts (and for a reason!). Of course you can still have array lists components in a record, which can be mutated (added to), but I'd say using records this way is non-idiomatic, can lead to suprising behavior (e.g. w.r.t. pattern matching) and is discouraged. A great writeup on why records are the way they are can be found here: https://nipafx.dev/java-record-semantics/ Cheers Maurizio On 25/01/2024 13:18, Julian Waters wrote: > Hi all, > > Just a quick question: > > Why isn't modifying the contents of a record supported formally? > (Without reflection and the like) Wouldn't it be helpful to be able to > modify a record's fields? Is there any plan to allow doing so in the > future? > > best regards, > Julian From kasperni at gmail.com Thu Jan 25 13:34:25 2024 From: kasperni at gmail.com (Kasper Nielsen) Date: Thu, 25 Jan 2024 14:34:25 +0100 Subject: Non final records In-Reply-To: References: Message-ID: Hi Julian, It is discussed here in some detail: https://www.infoq.com/articles/java-14-feature-spotlight/ /Kasper On Thu, 25 Jan 2024 at 14:19, Julian Waters wrote: > > Hi all, > > Just a quick question: > > Why isn't modifying the contents of a record supported formally? > (Without reflection and the like) Wouldn't it be helpful to be able to > modify a record's fields? Is there any plan to allow doing so in the > future? > > best regards, > Julian From redio.development at gmail.com Fri Jan 26 07:30:01 2024 From: redio.development at gmail.com (Red IO) Date: Fri, 26 Jan 2024 08:30:01 +0100 Subject: Pattern matching for java.util.Optional Message-ID: Now that pattern matching is nearly complete I was thinking about the distinction how the option type of the jdk works. What I'm talking about is the split between checking if it's empty and retrieving the value. Sure there are ways to transform the optional and GetOrElse and Throw varients. But none of them express the connection between isPresent and get: var option = Optional.of("Foo"); if (option.isPresent()) { var foo = option.get(); } Now pattern matching comes into play. A great solution that is used by many other languages to deal with the option type is matching rather or not it's present and in the same step providing the value to the valid code block. Something like this: var option = Optional.of("Foo"); switch(option) { case Some(foo) -> {} case None() -> {} } Or if(option instanceof Some(foo) { } This is indeed possible in the current version of java using sealed types, records and pattern matching. Now it's sad that Optional was introduced before the features we have now where available and introducing a second option type would be confusing and terrible for apis. But I don't think all hope is lost yet. I'm not too familiar with binary compatibility but from an api standpoint it would not change the public api of optional if it was turned from a final class into a sealed interface with 2 inner records representing the 2 states of the Optional instead of a nullable field. This way the public api of optional would just be added 2 inner types and otherwise stay the same. I would love to hear your feedback on this one. Great regards RedIODev -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Fri Jan 26 11:42:38 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 26 Jan 2024 06:42:38 -0500 Subject: Pattern matching for java.util.Optional In-Reply-To: References: Message-ID: <575311cf-f763-45b9-ac5e-096644edec2e@oracle.com> Let's step back, because your question is caught up in the details of a solution, rather than focusing on the problem. Obviously we want to be able to pattern match on Optional.? You've gone down the road of "We have pattern matching for ADTs, so let's turn Optional into an ADT, and then pattern match on that", and then got caught in the details of whether this is a compatible refactoring. But I don't think we have to, or should, refactor the representation to "match" the pattern machinery.? As outlined in "Towards Member Patterns" and "Patterns in the Object Model", we want to use the same tools for destructuring as we do for aggregation.? Optional is assembled with static factories; we should be able to take it apart with static patterns: ??? switch (o) { ??????? case Optional.of(var e): ... ??????? case Optional.empty(): ... ??? } Adding `Some` and `None` deconstruction patterns would work, but then we lose the correspondence between "how we put it together" and "how we take it apart."? So I don't want to go down this road.? (Another reason to not go down this road is that we want Optional to work as a value type, which means one implementation.) There are a few details yet to be wrapped up (e.g., how do we denote the of/empty pair as exhaustive). On 1/26/2024 2:30 AM, Red IO wrote: > Now that pattern matching is nearly complete I was thinking about the > distinction how the option type of the jdk works. > What I'm talking about is the split between checking if it's empty and > retrieving the value. > Sure there are ways to transform the optional and GetOrElse and Throw > varients. But none of them express the connection between isPresent > and get: > var option = Optional.of("Foo"); > if (option.isPresent()) { > var foo = option.get(); > } > Now pattern matching comes into play. A great solution that is used by > many other languages to deal with the option type is matching rather > or not it's present and in the same step providing the value to the > valid code block. Something like this: > var option = Optional.of("Foo"); > switch(option) { > case Some(foo) -> {} > case None() -> {} > } > Or > if(option instanceof Some(foo) { > > } > > This is indeed possible in the current version of java using sealed > types, records and pattern matching. > Now it's sad that Optional was introduced before the features we have > now where available and introducing a second option type would be > confusing and terrible for apis. > > But I don't think all hope is lost yet. I'm not too familiar with > binary compatibility but from an api standpoint it would not change > the public api of optional if it was turned from a final class into a > sealed interface with 2 inner records representing the 2 states of the > Optional instead of a nullable field. This way the public api of > optional would just be added 2 inner types and otherwise stay the same. > > I would love to hear your feedback on this one. > > Great regards > RedIODev -------------- next part -------------- An HTML attachment was scrubbed... URL: From sarma.swaranga at gmail.com Fri Jan 26 11:44:10 2024 From: sarma.swaranga at gmail.com (Swaranga Sarma) Date: Fri, 26 Jan 2024 03:44:10 -0800 Subject: Pattern matching for java.util.Optional In-Reply-To: References: Message-ID: >From what I have seen in some examples, I believe you will be able to do: switch(opt) { case Optional.of(var value) -> // use non-null value here case Optional.empty() -> // handle absent case } This will likely be possible with the introduction of deconstruction patterns: https://gist.github.com/babanin/9ddf60279a9cbe50260ddb11d1aa2e80#recovering-static-factory-arguments Regards Swaranga On Fri, Jan 26, 2024 at 12:35?AM Red IO wrote: > Now that pattern matching is nearly complete I was thinking about the > distinction how the option type of the jdk works. > What I'm talking about is the split between checking if it's empty and > retrieving the value. > Sure there are ways to transform the optional and GetOrElse and Throw > varients. But none of them express the connection between isPresent and get: > var option = Optional.of("Foo"); > if (option.isPresent()) { > var foo = option.get(); > } > Now pattern matching comes into play. A great solution that is used by > many other languages to deal with the option type is matching rather or not > it's present and in the same step providing the value to the valid code > block. Something like this: > var option = Optional.of("Foo"); > switch(option) { > case Some(foo) -> {} > case None() -> {} > } > Or > if(option instanceof Some(foo) { > > } > > This is indeed possible in the current version of java using sealed types, > records and pattern matching. > Now it's sad that Optional was introduced before the features we have now > where available and introducing a second option type would be confusing and > terrible for apis. > > But I don't think all hope is lost yet. I'm not too familiar with binary > compatibility but from an api standpoint it would not change the public api > of optional if it was turned from a final class into a sealed interface > with 2 inner records representing the 2 states of the Optional instead of a > nullable field. This way the public api of optional would just be added 2 > inner types and otherwise stay the same. > > I would love to hear your feedback on this one. > > Great regards > RedIODev > -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Fri Jan 26 14:43:20 2024 From: redio.development at gmail.com (Red IO) Date: Fri, 26 Jan 2024 15:43:20 +0100 Subject: Pattern matching for java.util.Optional In-Reply-To: <575311cf-f763-45b9-ac5e-096644edec2e@oracle.com> References: <575311cf-f763-45b9-ac5e-096644edec2e@oracle.com> Message-ID: I wasn't aware of the plan to support that kind of pattern matching in the future. Of course this is a way better and less disruptive way of doing it. Thanks for the insightful answer. One more thing what is your opinion on the sealed interface->class, class,... pattern to model union types in general. Is it a good strategy or an antipattern that should be avoided? Great regards RedIODev On Fri, Jan 26, 2024, 12:42 Brian Goetz wrote: > Let's step back, because your question is caught up in the details of a > solution, rather than focusing on the problem. > > Obviously we want to be able to pattern match on Optional. You've gone > down the road of "We have pattern matching for ADTs, so let's turn Optional > into an ADT, and then pattern match on that", and then got caught in the > details of whether this is a compatible refactoring. > > But I don't think we have to, or should, refactor the representation to > "match" the pattern machinery. As outlined in "Towards Member Patterns" > and "Patterns in the Object Model", we want to use the same tools for > destructuring as we do for aggregation. Optional is assembled with static > factories; we should be able to take it apart with static patterns: > > switch (o) { > case Optional.of(var e): ... > case Optional.empty(): ... > } > > Adding `Some` and `None` deconstruction patterns would work, but then we > lose the correspondence between "how we put it together" and "how we take > it apart." So I don't want to go down this road. (Another reason to not > go down this road is that we want Optional to work as a value type, which > means one implementation.) > > There are a few details yet to be wrapped up (e.g., how do we denote the > of/empty pair as exhaustive). > > On 1/26/2024 2:30 AM, Red IO wrote: > > Now that pattern matching is nearly complete I was thinking about the > distinction how the option type of the jdk works. > What I'm talking about is the split between checking if it's empty and > retrieving the value. > Sure there are ways to transform the optional and GetOrElse and Throw > varients. But none of them express the connection between isPresent and get: > var option = Optional.of("Foo"); > if (option.isPresent()) { > var foo = option.get(); > } > Now pattern matching comes into play. A great solution that is used by > many other languages to deal with the option type is matching rather or not > it's present and in the same step providing the value to the valid code > block. Something like this: > var option = Optional.of("Foo"); > switch(option) { > case Some(foo) -> {} > case None() -> {} > } > Or > if(option instanceof Some(foo) { > > } > > This is indeed possible in the current version of java using sealed types, > records and pattern matching. > Now it's sad that Optional was introduced before the features we have now > where available and introducing a second option type would be confusing and > terrible for apis. > > But I don't think all hope is lost yet. I'm not too familiar with binary > compatibility but from an api standpoint it would not change the public api > of optional if it was turned from a final class into a sealed interface > with 2 inner records representing the 2 states of the Optional instead of a > nullable field. This way the public api of optional would just be added 2 > inner types and otherwise stay the same. > > I would love to hear your feedback on this one. > > Great regards > RedIODev > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jan 26 15:51:23 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 26 Jan 2024 10:51:23 -0500 Subject: Pattern matching for java.util.Optional In-Reply-To: References: <575311cf-f763-45b9-ac5e-096644edec2e@oracle.com> Message-ID: Hello RedIODev, It is absolutely a good strategy. Sealed types are Java's tagged unions (or sum types, further reading here -- https://en.wikipedia.org/wiki/Tagged_union). And tagged unions are a form of union types. Anything you would want a union type for, a sealed type should fit the bill cleanly. Did you have a particular concern about using them that makes them look like a potential anti-pattern? Thank you for your time! David Alayachew On Fri, Jan 26, 2024 at 9:43?AM Red IO wrote: > I wasn't aware of the plan to support that kind of pattern matching in the > future. Of course this is a way better and less disruptive way of doing it. > Thanks for the insightful answer. > > One more thing what is your opinion on the sealed interface->class, > class,... pattern to model union types in general. Is it a good strategy or > an antipattern that should be avoided? > > Great regards > RedIODev > > On Fri, Jan 26, 2024, 12:42 Brian Goetz wrote: > >> Let's step back, because your question is caught up in the details of a >> solution, rather than focusing on the problem. >> >> Obviously we want to be able to pattern match on Optional. You've gone >> down the road of "We have pattern matching for ADTs, so let's turn Optional >> into an ADT, and then pattern match on that", and then got caught in the >> details of whether this is a compatible refactoring. >> >> But I don't think we have to, or should, refactor the representation to >> "match" the pattern machinery. As outlined in "Towards Member Patterns" >> and "Patterns in the Object Model", we want to use the same tools for >> destructuring as we do for aggregation. Optional is assembled with static >> factories; we should be able to take it apart with static patterns: >> >> switch (o) { >> case Optional.of(var e): ... >> case Optional.empty(): ... >> } >> >> Adding `Some` and `None` deconstruction patterns would work, but then we >> lose the correspondence between "how we put it together" and "how we take >> it apart." So I don't want to go down this road. (Another reason to not >> go down this road is that we want Optional to work as a value type, which >> means one implementation.) >> >> There are a few details yet to be wrapped up (e.g., how do we denote the >> of/empty pair as exhaustive). >> >> On 1/26/2024 2:30 AM, Red IO wrote: >> >> Now that pattern matching is nearly complete I was thinking about the >> distinction how the option type of the jdk works. >> What I'm talking about is the split between checking if it's empty and >> retrieving the value. >> Sure there are ways to transform the optional and GetOrElse and Throw >> varients. But none of them express the connection between isPresent and get: >> var option = Optional.of("Foo"); >> if (option.isPresent()) { >> var foo = option.get(); >> } >> Now pattern matching comes into play. A great solution that is used by >> many other languages to deal with the option type is matching rather or not >> it's present and in the same step providing the value to the valid code >> block. Something like this: >> var option = Optional.of("Foo"); >> switch(option) { >> case Some(foo) -> {} >> case None() -> {} >> } >> Or >> if(option instanceof Some(foo) { >> >> } >> >> This is indeed possible in the current version of java using sealed >> types, records and pattern matching. >> Now it's sad that Optional was introduced before the features we have now >> where available and introducing a second option type would be confusing and >> terrible for apis. >> >> But I don't think all hope is lost yet. I'm not too familiar with binary >> compatibility but from an api standpoint it would not change the public api >> of optional if it was turned from a final class into a sealed interface >> with 2 inner records representing the 2 states of the Optional instead of a >> nullable field. This way the public api of optional would just be added 2 >> inner types and otherwise stay the same. >> >> I would love to hear your feedback on this one. >> >> Great regards >> RedIODev >> >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From numeralnathan at gmail.com Fri Jan 26 16:17:05 2024 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Fri, 26 Jan 2024 09:17:05 -0700 Subject: Pattern matching for java.util.Optional In-Reply-To: <575311cf-f763-45b9-ac5e-096644edec2e@oracle.com> References: <575311cf-f763-45b9-ac5e-096644edec2e@oracle.com> Message-ID: Am I understanding this correctly or are we throwing an idea out there? Is the following legal today in Java 21? switch (o) { case Optional.of(var e): ... case Optional.empty(): ... } On Fri, Jan 26, 2024 at 6:28?AM Brian Goetz wrote: > Let's step back, because your question is caught up in the details of a > solution, rather than focusing on the problem. > > Obviously we want to be able to pattern match on Optional. You've gone > down the road of "We have pattern matching for ADTs, so let's turn Optional > into an ADT, and then pattern match on that", and then got caught in the > details of whether this is a compatible refactoring. > > But I don't think we have to, or should, refactor the representation to > "match" the pattern machinery. As outlined in "Towards Member Patterns" > and "Patterns in the Object Model", we want to use the same tools for > destructuring as we do for aggregation. Optional is assembled with static > factories; we should be able to take it apart with static patterns: > > switch (o) { > case Optional.of(var e): ... > case Optional.empty(): ... > } > > Adding `Some` and `None` deconstruction patterns would work, but then we > lose the correspondence between "how we put it together" and "how we take > it apart." So I don't want to go down this road. (Another reason to not > go down this road is that we want Optional to work as a value type, which > means one implementation.) > > There are a few details yet to be wrapped up (e.g., how do we denote the > of/empty pair as exhaustive). > > On 1/26/2024 2:30 AM, Red IO wrote: > > Now that pattern matching is nearly complete I was thinking about the > distinction how the option type of the jdk works. > What I'm talking about is the split between checking if it's empty and > retrieving the value. > Sure there are ways to transform the optional and GetOrElse and Throw > varients. But none of them express the connection between isPresent and get: > var option = Optional.of("Foo"); > if (option.isPresent()) { > var foo = option.get(); > } > Now pattern matching comes into play. A great solution that is used by > many other languages to deal with the option type is matching rather or not > it's present and in the same step providing the value to the valid code > block. Something like this: > var option = Optional.of("Foo"); > switch(option) { > case Some(foo) -> {} > case None() -> {} > } > Or > if(option instanceof Some(foo) { > > } > > This is indeed possible in the current version of java using sealed types, > records and pattern matching. > Now it's sad that Optional was introduced before the features we have now > where available and introducing a second option type would be confusing and > terrible for apis. > > But I don't think all hope is lost yet. I'm not too familiar with binary > compatibility but from an api standpoint it would not change the public api > of optional if it was turned from a final class into a sealed interface > with 2 inner records representing the 2 states of the Optional instead of a > nullable field. This way the public api of optional would just be added 2 > inner types and otherwise stay the same. > > I would love to hear your feedback on this one. > > Great regards > RedIODev > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From numeralnathan at gmail.com Fri Jan 26 16:25:04 2024 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Fri, 26 Jan 2024 09:25:04 -0700 Subject: JEP: Derived Record Creation: Another Method Message-ID: I am looking at JEP Derived Record Creation. https://openjdk.org/jeps/8321133 I see the following example. Point nextLoc = oldLoc with { x *= 2; y *= 2; z *= 2; }; If the body of the wither becomes more complex, then this will make reading the code difficult. For example, there could be a deep nesting of wither. Lambdas can also be nested. However, the best practice is to move multiline lambda bodies into a separate method and leave behind a single line that calls the method. The ability to move the lambda bodies to another method allows for simplification of the code and improves readability. Can we change the design to allow withers to be moved to another method? I ask without thinking through the ramifications. If withers can't be moved to another method, how do we eliminate nesting and other complexities? -------------- next part -------------- An HTML attachment was scrubbed... URL: From duke at openjdk.org Fri Jan 26 16:30:50 2024 From: duke at openjdk.org (David Alayachew) Date: Fri, 26 Jan 2024 16:30:50 GMT Subject: [amber-docs] RFR: Fixing the incorrect code example Message-ID: There are only 3 methods on java.util.regex.Matcher that have the name group. Here they are. https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Matcher.html#group() https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Matcher.html#group(int) https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Matcher.html#group(java.lang.String) And since this is an IntStream, the only possible method that could be matched is the 2nd link above. Based on that, it is clear that the code example meant to put an `m` instead of `Matcher`. ------------- Commit messages: - Fixing the incorrect code example Changes: https://git.openjdk.org/amber-docs/pull/23/files Webrev: https://webrevs.openjdk.org/?repo=amber-docs&pr=23&range=00 Stats: 1 line in 1 file changed: 0 ins; 0 del; 1 mod Patch: https://git.openjdk.org/amber-docs/pull/23.diff Fetch: git fetch https://git.openjdk.org/amber-docs.git pull/23/head:pull/23 PR: https://git.openjdk.org/amber-docs/pull/23 From brian.goetz at oracle.com Fri Jan 26 17:09:16 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Fri, 26 Jan 2024 12:09:16 -0500 Subject: Pattern matching for java.util.Optional In-Reply-To: References: <575311cf-f763-45b9-ac5e-096644edec2e@oracle.com> Message-ID: It is not currently allowed, being discussed on a-s-e. On 1/26/2024 11:17 AM, Nathan Reynolds wrote: > Am I understanding this correctly or are we throwing an idea out > there?? Is the following legal today in Java 21? > > ??? switch (o) { > ??????? case Optional.of(var e): ... > ??????? case Optional.empty(): ... > ??? } > > On Fri, Jan 26, 2024 at 6:28?AM Brian Goetz > wrote: > > Let's step back, because your question is caught up in the details > of a solution, rather than focusing on the problem. > > Obviously we want to be able to pattern match on Optional.? You've > gone down the road of "We have pattern matching for ADTs, so let's > turn Optional into an ADT, and then pattern match on that", and > then got caught in the details of whether this is a compatible > refactoring. > > But I don't think we have to, or should, refactor the > representation to "match" the pattern machinery.? As outlined in > "Towards Member Patterns" and "Patterns in the Object Model", we > want to use the same tools for destructuring as we do for > aggregation.? Optional is assembled with static factories; we > should be able to take it apart with static patterns: > > ??? switch (o) { > ??????? case Optional.of(var e): ... > ??????? case Optional.empty(): ... > ??? } > > Adding `Some` and `None` deconstruction patterns would work, but > then we lose the correspondence between "how we put it together" > and "how we take it apart."? So I don't want to go down this > road.? (Another reason to not go down this road is that we want > Optional to work as a value type, which means one implementation.) > > There are a few details yet to be wrapped up (e.g., how do we > denote the of/empty pair as exhaustive). > > On 1/26/2024 2:30 AM, Red IO wrote: >> Now that pattern matching is nearly complete I was thinking about >> the distinction how the option type of the jdk works. >> What I'm talking about is the split between checking if it's >> empty and retrieving the value. >> Sure there are ways to transform the optional and GetOrElse and >> Throw varients. But none of them express the connection between >> isPresent and get: >> var option = Optional.of("Foo"); >> if (option.isPresent()) { >> var foo = option.get(); >> } >> Now pattern matching comes into play. A great solution that is >> used by many other languages to deal with the option type is >> matching rather or not it's present and in the same step >> providing the value to the valid code block. Something like this: >> var option = Optional.of("Foo"); >> switch(option) { >> case Some(foo) -> {} >> case None() -> {} >> } >> Or >> if(option instanceof Some(foo) { >> >> } >> >> This is indeed possible in the current version of java using >> sealed types, records and pattern matching. >> Now it's sad that Optional was introduced before the features we >> have now where available and introducing a second option type >> would be confusing and terrible for apis. >> >> But I don't think all hope is lost yet. I'm not too familiar with >> binary compatibility but from an api standpoint it would not >> change the public api of optional if it was turned from a final >> class into a sealed interface with 2 inner records representing >> the 2 states of the Optional instead of a nullable field. This >> way the public api of optional would just be added 2 inner types >> and otherwise stay the same. >> >> I would love to hear your feedback on this one. >> >> Great regards >> RedIODev > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jan 26 19:18:48 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 26 Jan 2024 14:18:48 -0500 Subject: JEP: Derived Record Creation: Another Method In-Reply-To: References: Message-ID: Hello Nathan, Wasn't the entire point of withers to simplify immutable transformation? As in, let's make the simple case easy and obvious from a glance. What you seem to be describing is the worst case scenario. You are describing a transformation where most or all of the fields need to be transformed in a very complex record in very complex ways. Is that not the purpose of more general deconstruction and reconstruction functionality? Let's pretend that your example given is super complex, and we would prefer to transport the logic to a separate method. In my mind, I would approach it like so. if (oldLoc instanceof Point(int x, int y, int z)) { final Point nextLoc = complexTransformationLogic(x, y, z); } It's sort of like how switch and when clauses do not obviate the need for an if statement with instanceof. It only smooths out the happy path and provides you a few more benefits. If statements are still going to be more flexible than switch. And in this case, I think the same can be said for if statements vs withers. And either way, if that is not enough, you can even do this. final Point nextLoc = complexTransformationLogic(oldLoc); This allows you to truly maximize the reuse. And aside from the overhead, it seems to be semantically equivalent to what you proposed. And addressing your nesting point, you can just nest the logic inside of the newly created method, and then break that out into its own method too. You should still have feature parity with what you described. How do you feel about this? Thank you for reaching out! David Alayachew On Fri, Jan 26, 2024 at 1:05?PM Nathan Reynolds wrote: > I am looking at JEP Derived Record Creation. > https://openjdk.org/jeps/8321133 I see the following example. > > Point nextLoc = oldLoc with { > x *= 2; > y *= 2; > z *= 2; > }; > > If the body of the wither becomes more complex, then this will make reading the code difficult. For example, there could be a deep nesting of wither. > > Lambdas can also be nested. However, the best practice is to move multiline lambda bodies into a separate method and leave behind a single line that calls the method. The ability to move the lambda bodies to another method allows for simplification of the code and improves readability. > > Can we change the design to allow withers to be moved to another method? I ask without thinking through the ramifications. If withers can't be moved to another method, how do we eliminate nesting and other complexities? > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From numeralnathan at gmail.com Fri Jan 26 21:03:04 2024 From: numeralnathan at gmail.com (Nathan Reynolds) Date: Fri, 26 Jan 2024 14:03:04 -0700 Subject: JEP: Derived Record Creation: Another Method In-Reply-To: References: Message-ID: That sounds great! I am glad there is an easy clean solution. On Fri, Jan 26, 2024 at 12:19?PM David Alayachew wrote: > Hello Nathan, > > Wasn't the entire point of withers to simplify immutable transformation? > As in, let's make the simple case easy and obvious from a glance. > > What you seem to be describing is the worst case scenario. You are > describing a transformation where most or all of the fields need to be > transformed in a very complex record in very complex ways. Is that not the > purpose of more general deconstruction and reconstruction functionality? > > Let's pretend that your example given is super complex, and we would > prefer to transport the logic to a separate method. > > In my mind, I would approach it like so. > > if (oldLoc instanceof Point(int x, int y, int z)) > { > > final Point nextLoc = complexTransformationLogic(x, y, z); > > } > It's sort of like how switch and when clauses do not obviate the need for > an if statement with instanceof. It only smooths out the happy path and > provides you a few more benefits. If statements are still going to be more > flexible than switch. And in this case, I think the same can be said for if > statements vs withers. > > And either way, if that is not enough, you can even do this. > > final Point nextLoc = complexTransformationLogic(oldLoc); > > This allows you to truly maximize the reuse. And aside from the overhead, > it seems to be semantically equivalent to what you proposed. And addressing > your nesting point, you can just nest the logic inside of the newly created > method, and then break that out into its own method too. You should still > have feature parity with what you described. > > How do you feel about this? > > Thank you for reaching out! > David Alayachew > > On Fri, Jan 26, 2024 at 1:05?PM Nathan Reynolds > wrote: > >> I am looking at JEP Derived Record Creation. >> https://openjdk.org/jeps/8321133 I see the following example. >> >> Point nextLoc = oldLoc with { >> x *= 2; >> y *= 2; >> z *= 2; >> }; >> >> If the body of the wither becomes more complex, then this will make reading the code difficult. For example, there could be a deep nesting of wither. >> >> Lambdas can also be nested. However, the best practice is to move multiline lambda bodies into a separate method and leave behind a single line that calls the method. The ability to move the lambda bodies to another method allows for simplification of the code and improves readability. >> >> Can we change the design to allow withers to be moved to another method? I ask without thinking through the ramifications. If withers can't be moved to another method, how do we eliminate nesting and other complexities? >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Jan 26 22:39:35 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 26 Jan 2024 17:39:35 -0500 Subject: JEP: Derived Record Creation: Another Method In-Reply-To: References: Message-ID: ? David reacted via Gmail On Fri, Jan 26, 2024, 4:03 PM Nathan Reynolds wrote: > That sounds great! I am glad there is an easy clean solution. > > On Fri, Jan 26, 2024 at 12:19?PM David Alayachew > wrote: > >> Hello Nathan, >> >> Wasn't the entire point of withers to simplify immutable transformation? >> As in, let's make the simple case easy and obvious from a glance. >> >> What you seem to be describing is the worst case scenario. You are >> describing a transformation where most or all of the fields need to be >> transformed in a very complex record in very complex ways. Is that not the >> purpose of more general deconstruction and reconstruction functionality? >> >> Let's pretend that your example given is super complex, and we would >> prefer to transport the logic to a separate method. >> >> In my mind, I would approach it like so. >> >> if (oldLoc instanceof Point(int x, int y, int z)) >> { >> >> final Point nextLoc = complexTransformationLogic(x, y, z); >> >> } >> It's sort of like how switch and when clauses do not obviate the need for >> an if statement with instanceof. It only smooths out the happy path and >> provides you a few more benefits. If statements are still going to be more >> flexible than switch. And in this case, I think the same can be said for if >> statements vs withers. >> >> And either way, if that is not enough, you can even do this. >> >> final Point nextLoc = complexTransformationLogic(oldLoc); >> >> This allows you to truly maximize the reuse. And aside from the overhead, >> it seems to be semantically equivalent to what you proposed. And addressing >> your nesting point, you can just nest the logic inside of the newly created >> method, and then break that out into its own method too. You should still >> have feature parity with what you described. >> >> How do you feel about this? >> >> Thank you for reaching out! >> David Alayachew >> >> On Fri, Jan 26, 2024 at 1:05?PM Nathan Reynolds >> wrote: >> >>> I am looking at JEP Derived Record Creation. >>> https://openjdk.org/jeps/8321133 I see the following example. >>> >>> Point nextLoc = oldLoc with { >>> x *= 2; >>> y *= 2; >>> z *= 2; >>> }; >>> >>> If the body of the wither becomes more complex, then this will make reading the code difficult. For example, there could be a deep nesting of wither. >>> >>> Lambdas can also be nested. However, the best practice is to move multiline lambda bodies into a separate method and leave behind a single line that calls the method. The ability to move the lambda bodies to another method allows for simplification of the code and improves readability. >>> >>> Can we change the design to allow withers to be moved to another method? I ask without thinking through the ramifications. If withers can't be moved to another method, how do we eliminate nesting and other complexities? >>> >>> -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: text/vnd.google.email-reaction+json Size: 37 bytes Desc: not available URL: -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Tue Jan 30 04:11:23 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 29 Jan 2024 23:11:23 -0500 Subject: Is it feasible for Exhaustiveness Checks to tell us what we are missing? At least in some cases? Message-ID: Hello Amber Dev Team, The previous discussions on Checked Exceptions reminded me about this. Is there any possible future where sealed types will be able to tell you which permitted subtypes are missing? This is actually one of the sleeper features of Checked Exceptions -- they don't just give you exhaustiveness, they are even nice enough to tell you exactly which Exception type you left out. This is a godsend for maintenance, fearless refactoring, and general correctness. Any chance we could get this for sealed types in the future? Thank you for your time and help! David Alayachew -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Jan 30 08:21:09 2024 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 30 Jan 2024 09:21:09 +0100 Subject: Is it feasible for Exhaustiveness Checks to tell us what we are missing? At least in some cases? In-Reply-To: References: Message-ID: IntelliJ IDEA can do this in simple cases (use 'Create missing branches' action in Alt+Enter menu on "statement does not cover all possible input values" error message). From what I remember, it could be very non-trivial to determine a minimal set of missing branches in a general case, especially if you have generic parameters and nested deconstruction patterns. I'm not sure but probably it's an NP-complete problem. With best regards, Tagir Valeev. On Tue, Jan 30, 2024 at 6:40?AM David Alayachew wrote: > Hello Amber Dev Team, > > The previous discussions on Checked Exceptions reminded me about this. > > Is there any possible future where sealed types will be able to tell you > which permitted subtypes are missing? > > This is actually one of the sleeper features of Checked Exceptions -- they > don't just give you exhaustiveness, they are even nice enough to tell you > exactly which Exception type you left out. > > This is a godsend for maintenance, fearless refactoring, and general > correctness. > > Any chance we could get this for sealed types in the future? > > Thank you for your time and help! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Wed Jan 31 03:55:11 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Tue, 30 Jan 2024 22:55:11 -0500 Subject: Is it feasible for Exhaustiveness Checks to tell us what we are missing? At least in some cases? In-Reply-To: References: Message-ID: Hello Tagir, Thank you for your response! Fair point about NP-Complete. Maybe scaling back the idea would make more sense. But I am confused about this comment. > From what I remember, it could be very non-trivial to determine a minimal set of missing branches I'm very surprised to hear this, but if that is true, then let's scale this down to the absolute minimum. In order to know that we are missing a case, we must first find a gap in the exhaustiveness, with respect to the remainder. So let's move the goal posts and say that if we can report that single type that is the gap, I would be happy enough with that. Would that be more realistic? Thank you for reaching out! David Alayachew On Tue, Jan 30, 2024 at 3:21?AM Tagir Valeev wrote: > IntelliJ IDEA can do this in simple cases (use 'Create missing branches' > action in Alt+Enter menu on "statement does not cover all possible input > values" error message). From what I remember, it could be very non-trivial > to determine a minimal set of missing branches in a general case, > especially if you have generic parameters and nested deconstruction > patterns. I'm not sure but probably it's an NP-complete problem. > > With best regards, > Tagir Valeev. > > On Tue, Jan 30, 2024 at 6:40?AM David Alayachew > wrote: > >> Hello Amber Dev Team, >> >> The previous discussions on Checked Exceptions reminded me about this. >> >> Is there any possible future where sealed types will be able to tell you >> which permitted subtypes are missing? >> >> This is actually one of the sleeper features of Checked Exceptions -- >> they don't just give you exhaustiveness, they are even nice enough to tell >> you exactly which Exception type you left out. >> >> This is a godsend for maintenance, fearless refactoring, and general >> correctness. >> >> Any chance we could get this for sealed types in the future? >> >> Thank you for your time and help! >> David Alayachew >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Wed Jan 31 11:11:05 2024 From: brian.goetz at oracle.com (Brian Goetz) Date: Wed, 31 Jan 2024 06:11:05 -0500 Subject: Is it feasible for Exhaustiveness Checks to tell us what we are missing? At least in some cases? In-Reply-To: References: Message-ID: I think you are probably thinking about this in too few dimensions.? For a one-dimensional problem: ??? sealed interface X permits A, B, C { } ??? switch (x) { ??????? case A a -> ??????? case B b -> ... ??? } it seems quite easy to "find the missing case."? But here's an only slightly harder one: ??? sealed interface A permits T, U { } ??? sealed interface B permits V, W { } ??? record R(A a, B b) { } ??? switch (r) { ??????? case R(A a, V b) -> ... ??????? case R(T a, _)?? -> ... ??? } What's missing?? Well, there are any number of cases that can complete the picture, such as ? ? ? ? case R(U a, W b) -> ... The problem gets harder fast as the dimensionality increases; R has two components, both of which use sealing.? And things scale with not only the number of components, but the depth of sealed hierarchies, etc. If you read the specification (or the precursor mathematical model, an early version of which is here (https://cr.openjdk.org/~briangoetz/eg-attachments/Coverage.pdf), you'll see that it does not construct the full product space and start "checking off boxes", so turning it into an example generator (and keeping the two consistent) is not a small task. On 1/30/2024 10:55 PM, David Alayachew wrote: > Hello Tagir, > > Thank you for your response! > > Fair point about NP-Complete. Maybe scaling back the idea would make > more sense. > > But I am confused about this comment. > > > From what I remember, it could be very non-trivial to determine a > minimal set of missing branches > > I'm very surprised to hear this, but if that is true, then let's scale > this down to the absolute minimum. > > In order to know that we are missing a case, we must first find a gap > in the exhaustiveness, with respect to the remainder. > > So let's move the goal posts and say that if we can report that single > type that is the gap, I would be happy enough with that. > > Would that be more realistic? > > Thank you for reaching out! > David Alayachew > > On Tue, Jan 30, 2024 at 3:21?AM Tagir Valeev wrote: > > IntelliJ IDEA can do this in simple cases (use 'Create missing > branches' action in Alt+Enter menu on "statement does not cover > all possible input values" error message). From what I remember, > it could be very non-trivial to determine a minimal set of missing > branches in a general case, especially if you have generic > parameters and nested deconstruction patterns. I'm not sure but > probably it's an NP-complete problem. > > With best regards, > Tagir Valeev. > > On Tue, Jan 30, 2024 at 6:40?AM David Alayachew > wrote: > > Hello Amber Dev Team, > > The previous discussions on Checked Exceptions reminded me > about this. > > Is there any possible future where sealed types will be able > to tell you which permitted subtypes are missing? > > This is actually one of the sleeper features of Checked > Exceptions -- they don't just give you exhaustiveness, they > are even nice enough to tell you exactly which Exception type > you left out. > > This is a godsend for maintenance, fearless refactoring, and > general correctness. > > Any chance we could get this for sealed types in the future? > > Thank you for your time and help! > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Wed Jan 31 11:30:23 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Wed, 31 Jan 2024 06:30:23 -0500 Subject: Is it feasible for Exhaustiveness Checks to tell us what we are missing? At least in some cases? In-Reply-To: References: Message-ID: ? David Alayachew reacted via Gmail On Wed, Jan 31, 2024 at 6:11?AM Brian Goetz wrote: > I think you are probably thinking about this in too few dimensions. For a > one-dimensional problem: > > sealed interface X permits A, B, C { } > > switch (x) { > case A a -> > case B b -> ... > } > > it seems quite easy to "find the missing case." But here's an only > slightly harder one: > > sealed interface A permits T, U { } > sealed interface B permits V, W { } > record R(A a, B b) { } > > switch (r) { > case R(A a, V b) -> ... > case R(T a, _) -> ... > } > > What's missing? Well, there are any number of cases that can complete the > picture, such as > > case R(U a, W b) -> ... > > The problem gets harder fast as the dimensionality increases; R has two > components, both of which use sealing. And things scale with not only the > number of components, but the depth of sealed hierarchies, etc. > > If you read the specification (or the precursor mathematical model, an > early version of which is here ( > https://cr.openjdk.org/~briangoetz/eg-attachments/Coverage.pdf), you'll > see that it does not construct the full product space and start "checking > off boxes", so turning it into an example generator (and keeping the two > consistent) is not a small task. > > > On 1/30/2024 10:55 PM, David Alayachew wrote: > > Hello Tagir, > > Thank you for your response! > > Fair point about NP-Complete. Maybe scaling back the idea would make more > sense. > > But I am confused about this comment. > > > From what I remember, it could be very non-trivial to determine a > minimal set of missing branches > > I'm very surprised to hear this, but if that is true, then let's scale > this down to the absolute minimum. > > In order to know that we are missing a case, we must first find a gap in > the exhaustiveness, with respect to the remainder. > > So let's move the goal posts and say that if we can report that single > type that is the gap, I would be happy enough with that. > > Would that be more realistic? > > Thank you for reaching out! > David Alayachew > > On Tue, Jan 30, 2024 at 3:21?AM Tagir Valeev wrote: > >> IntelliJ IDEA can do this in simple cases (use 'Create missing branches' >> action in Alt+Enter menu on "statement does not cover all possible input >> values" error message). From what I remember, it could be very non-trivial >> to determine a minimal set of missing branches in a general case, >> especially if you have generic parameters and nested deconstruction >> patterns. I'm not sure but probably it's an NP-complete problem. >> >> With best regards, >> Tagir Valeev. >> >> On Tue, Jan 30, 2024 at 6:40?AM David Alayachew >> wrote: >> >>> Hello Amber Dev Team, >>> >>> The previous discussions on Checked Exceptions reminded me about this. >>> >>> Is there any possible future where sealed types will be able to tell you >>> which permitted subtypes are missing? >>> >>> This is actually one of the sleeper features of Checked Exceptions -- >>> they don't just give you exhaustiveness, they are even nice enough to tell >>> you exactly which Exception type you left out. >>> >>> This is a godsend for maintenance, fearless refactoring, and general >>> correctness. >>> >>> Any chance we could get this for sealed types in the future? >>> >>> Thank you for your time and help! >>> David Alayachew >>> >> > -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: text/vnd.google.email-reaction+json Size: 37 bytes Desc: not available URL: -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Wed Jan 31 12:00:28 2024 From: davidalayachew at gmail.com (David Alayachew) Date: Wed, 31 Jan 2024 07:00:28 -0500 Subject: Is it feasible for Exhaustiveness Checks to tell us what we are missing? At least in some cases? In-Reply-To: References: Message-ID: Hello Brian, Thank you for your response! Special thanks for the link to the doc especially. That was illuminating. That said, it does provide an opportunity to move the goal posts even further. In your doc, we introduce the concept of P to represent the set of patterns. We then apply that set of patterns to various parts of the target (for records, its components, because, like the doc said - if you match the components, you match the record). Essentially, the doc says that it takes the set of patterns, looks at the target to see "what needs to be matched in order to be exhaustive", and then starts matching. But that's the point -- it can tell you when certain parts don't match. So what if we just constrained ourselves down to just that? Just telling the user that a specific part doesn't match? Using your example from above, what if we moved the goal posts even further and just said something like the second component on the record has not been fully covered? It doesn't have to tell us what to fill that hole with -- it just needs to tell us that there is a hole there. Thank you for the help! David Alayachew On Wed, Jan 31, 2024 at 6:11?AM Brian Goetz wrote: > I think you are probably thinking about this in too few dimensions. For a > one-dimensional problem: > > sealed interface X permits A, B, C { } > > switch (x) { > case A a -> > case B b -> ... > } > > it seems quite easy to "find the missing case." But here's an only > slightly harder one: > > sealed interface A permits T, U { } > sealed interface B permits V, W { } > record R(A a, B b) { } > > switch (r) { > case R(A a, V b) -> ... > case R(T a, _) -> ... > } > > What's missing? Well, there are any number of cases that can complete the > picture, such as > > case R(U a, W b) -> ... > > The problem gets harder fast as the dimensionality increases; R has two > components, both of which use sealing. And things scale with not only the > number of components, but the depth of sealed hierarchies, etc. > > If you read the specification (or the precursor mathematical model, an > early version of which is here ( > https://cr.openjdk.org/~briangoetz/eg-attachments/Coverage.pdf), you'll > see that it does not construct the full product space and start "checking > off boxes", so turning it into an example generator (and keeping the two > consistent) is not a small task. > > > On 1/30/2024 10:55 PM, David Alayachew wrote: > > Hello Tagir, > > Thank you for your response! > > Fair point about NP-Complete. Maybe scaling back the idea would make more > sense. > > But I am confused about this comment. > > > From what I remember, it could be very non-trivial to determine a > minimal set of missing branches > > I'm very surprised to hear this, but if that is true, then let's scale > this down to the absolute minimum. > > In order to know that we are missing a case, we must first find a gap in > the exhaustiveness, with respect to the remainder. > > So let's move the goal posts and say that if we can report that single > type that is the gap, I would be happy enough with that. > > Would that be more realistic? > > Thank you for reaching out! > David Alayachew > > On Tue, Jan 30, 2024 at 3:21?AM Tagir Valeev wrote: > >> IntelliJ IDEA can do this in simple cases (use 'Create missing branches' >> action in Alt+Enter menu on "statement does not cover all possible input >> values" error message). From what I remember, it could be very non-trivial >> to determine a minimal set of missing branches in a general case, >> especially if you have generic parameters and nested deconstruction >> patterns. I'm not sure but probably it's an NP-complete problem. >> >> With best regards, >> Tagir Valeev. >> >> On Tue, Jan 30, 2024 at 6:40?AM David Alayachew >> wrote: >> >>> Hello Amber Dev Team, >>> >>> The previous discussions on Checked Exceptions reminded me about this. >>> >>> Is there any possible future where sealed types will be able to tell you >>> which permitted subtypes are missing? >>> >>> This is actually one of the sleeper features of Checked Exceptions -- >>> they don't just give you exhaustiveness, they are even nice enough to tell >>> you exactly which Exception type you left out. >>> >>> This is a godsend for maintenance, fearless refactoring, and general >>> correctness. >>> >>> Any chance we could get this for sealed types in the future? >>> >>> Thank you for your time and help! >>> David Alayachew >>> >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: